diff mbox series

Add renameat2 function [BZ #17662]

Message ID 20180630121447.E4C8643994575@oldenburg.str.redhat.com
State New
Headers show
Series Add renameat2 function [BZ #17662] | expand

Commit Message

Florian Weimer June 30, 2018, 12:14 p.m. UTC
The implementation falls back to renameat if renameat2 is not available
in the kernel (or in the kernel headers) and the flags argument is zero.
Without kernel support, a non-zero argument returns EINVAL, not ENOSYS.
This mirrors what the kernel does for invalid renameat2 flags.

2018-06-30  Florian Weimer  <fweimer@redhat.com>

	[BZ # 17662]
	* libio/stdio.h [__USE_GNU] (RENAME_NOREPLACE, RENAME_EXCHANGE)
	(RENAME_WHITEOUT): Define.
	[__USE_GNU] (renameat2): Declare.
	* stdio-common/Makefile (routines): Add renameat2.
	(tests): Add tst-renameat2.
	* stdio-common/Versions (GLIBC_2_28): Export renameat2.
	* stdio-common/renameat2.c: New file.
	* stdio-common/tst-renameat2.c: Likewise.
	* sysdeps/unix/sysv/linux/renameat2.c: Likewise.
	* manual/filesys.texi (Temporary Files): Note that renameat2 is
	undocumented.
	* sysdeps/unix/sysv/linux/kernel-features.h
	[__LINUX_KERNEL_VERSION >= 0x030F00] (__ASSUME_RENAMEAT2): Define.
	* include/stdio.h (__renameat): Add alias for renameat.
	* stdio-common/renameat.c (__renameat): Rename from renameat.
	Add hidden definition and alias.
	* sysdeps/unix/sysv/linux/renameat.c: Likewise.
	* sysdeps/mach/hurd/renameat.c: Likewise.
	* sysdeps/**/libc*.abilist: Add renameat2.

Comments

Joseph Myers June 30, 2018, 8:21 p.m. UTC | #1
On Sat, 30 Jun 2018, Florian Weimer wrote:

> diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
> index b90ea30195..ab42c5845a 100644
> --- a/sysdeps/unix/sysv/linux/kernel-features.h
> +++ b/sysdeps/unix/sysv/linux/kernel-features.h
> @@ -144,3 +144,8 @@
>     */
>  
>  #define __ASSUME_CLONE_DEFAULT 1
> +
> +/* Support for the renameat2 system call was added in kernel 3.15.  */
> +#if __LINUX_KERNEL_VERSION >= 0x030F00
> +# define __ASSUME_RENAMEAT2
> +#endif

But for sparc in 3.16, alpha and microblaze in 3.17 and sh in 4.8.  So 
those need conditional #undefs in their kernel-features.h files.  
(Generically when adding __ASSUME_*, or when later removing such 
__ASSUME_* that should apparently be unconditional based on the minimum 
supported kernel, you need to watch out for such cases of some 
architectures having got a syscall later than others.)
Florian Weimer June 30, 2018, 9:10 p.m. UTC | #2
On 06/30/2018 10:21 PM, Joseph Myers wrote:
> On Sat, 30 Jun 2018, Florian Weimer wrote:
> 
>> diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
>> index b90ea30195..ab42c5845a 100644
>> --- a/sysdeps/unix/sysv/linux/kernel-features.h
>> +++ b/sysdeps/unix/sysv/linux/kernel-features.h
>> @@ -144,3 +144,8 @@
>>      */
>>   
>>   #define __ASSUME_CLONE_DEFAULT 1
>> +
>> +/* Support for the renameat2 system call was added in kernel 3.15.  */
>> +#if __LINUX_KERNEL_VERSION >= 0x030F00
>> +# define __ASSUME_RENAMEAT2
>> +#endif
> 
> But for sparc in 3.16, alpha and microblaze in 3.17 and sh in 4.8.  So
> those need conditional #undefs in their kernel-features.h files.
> (Generically when adding __ASSUME_*, or when later removing such
> __ASSUME_* that should apparently be unconditional based on the minimum
> supported kernel, you need to watch out for such cases of some
> architectures having got a syscall later than others.)

Ugh, I had forgotten about that.  Updated patch attached.

Is there any good source of information for this?  It's kind of 
complicated because modern architectures (such as nios2) use the 
centralized table, so they won't list new system calls, and for Aarch32 
and perhaps others, you need to check both arch/arm and arch/arm64 
because the compat system calls in the latter are apparently maintained 
separately.

Thanks,
Florian
Subject: [PATCH] Add renameat2 function [BZ #17662]
To: libc-alpha@sourceware.org

The implementation falls back to renameat if renameat2 is not available
in the kernel (or in the kernel headers) and the flags argument is zero.
Without kernel support, a non-zero argument returns EINVAL, not ENOSYS.
This mirrors what the kernel does for invalid renameat2 flags.

2018-06-30  Florian Weimer  <fweimer@redhat.com>

	[BZ # 17662]
	* libio/stdio.h [__USE_GNU] (RENAME_NOREPLACE, RENAME_EXCHANGE)
	(RENAME_WHITEOUT): Define.
	[__USE_GNU] (renameat2): Declare.
	* stdio-common/Makefile (routines): Add renameat2.
	(tests): Add tst-renameat2.
	* stdio-common/Versions (GLIBC_2_28): Export renameat2.
	* stdio-common/renameat2.c: New file.
	* stdio-common/tst-renameat2.c: Likewise.
	* sysdeps/unix/sysv/linux/renameat2.c: Likewise.
	* manual/filesys.texi (Temporary Files): Note that renameat2 is
	undocumented.
	* sysdeps/unix/sysv/linux/kernel-features.h
	[__LINUX_KERNEL_VERSION >= 0x030F00] (__ASSUME_RENAMEAT2): Define.
	* sysdeps/unix/sysv/linux/alpha/kernel-features.h
	[__LINUX_KERNEL_VERSION < 0x031100] (__ASSUME_RENAMEAT2): Undefine.
	* sysdeps/unix/sysv/linux/microblaze/kernel-features.h
	[__LINUX_KERNEL_VERSION < 0x031100] (__ASSUME_RENAMEAT2): Undefine.
	* sysdeps/unix/sysv/linux/sh/kernel-features.h
	[__LINUX_KERNEL_VERSION < 0x040800] (__ASSUME_RENAMEAT2): Undefine.
	* sysdeps/unix/sysv/linux/sparc/kernel-features.h
	[__LINUX_KERNEL_VERSION < 0x031000] (__ASSUME_RENAMEAT2): Undefine.
	* include/stdio.h (__renameat): Add alias for renameat.
	* stdio-common/renameat.c (__renameat): Rename from renameat.
	Add hidden definition and alias.
	* sysdeps/unix/sysv/linux/renameat.c: Likewise.
	* sysdeps/mach/hurd/renameat.c: Likewise.
	* sysdeps/**/libc*.abilist: Add renameat2.

diff --git a/NEWS b/NEWS
index a27dd371a3..093f364c7e 100644
--- a/NEWS
+++ b/NEWS
@@ -35,6 +35,8 @@ Major new features:
 * Building and running on GNU/Hurd systems now works without out-of-tree
   patches.
 
+* The renameat2 function has been added.
+
 * IDN domain names in getaddrinfo and getnameinfo now use the system libidn2
   library if installed.  libidn2 version 2.0.5 or later is recommended.  If
   libidn2 is not available, internationalized domain names are not encoded
diff --git a/include/stdio.h b/include/stdio.h
index f140813ad6..3ba0edc924 100644
--- a/include/stdio.h
+++ b/include/stdio.h
@@ -237,5 +237,8 @@ __putc_unlocked (int __c, FILE *__stream)
 }
 #  endif
 
+extern __typeof (renameat) __renameat;
+libc_hidden_proto (__renameat)
+
 # endif /* not _ISOMAC */
 #endif /* stdio.h */
diff --git a/libio/stdio.h b/libio/stdio.h
index 731f8e56f4..3cdc8cc532 100644
--- a/libio/stdio.h
+++ b/libio/stdio.h
@@ -153,6 +153,18 @@ extern int renameat (int __oldfd, const char *__old, int __newfd,
 		     const char *__new) __THROW;
 #endif
 
+#ifdef __USE_GNU
+/* Flags for renameat.  */
+# define RENAME_NOREPLACE (1 << 0)
+# define RENAME_EXCHANGE (1 << 1)
+# define RENAME_WHITEOUT (1 << 2)
+
+/* Rename file OLD relative to OLDFD to NEW relative to NEWFD, with
+   additional flags.  */
+extern int renameat2 (int __oldfd, const char *__old, int __newfd,
+		      const char *__new, unsigned int __flags) __THROW;
+#endif
+
 /* Create a temporary file and open it read/write.
 
    This function is a possible cancellation point and therefore not
diff --git a/manual/filesys.texi b/manual/filesys.texi
index cc70a6b7ee..db2f1041de 100644
--- a/manual/filesys.texi
+++ b/manual/filesys.texi
@@ -3552,6 +3552,7 @@ The @code{mkdtemp} function comes from OpenBSD.
 @c open_by_handle_at
 @c readlinkat
 @c renameat
+@c renameat2
 @c scandirat
 @c symlinkat
 @c unlinkat
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 738a3cead0..6fd628708f 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -35,7 +35,7 @@ routines	:=							      \
 	perror psignal							      \
 	tmpfile tmpfile64 tmpnam tmpnam_r tempnam tempname		      \
 	getline getw putw						      \
-	remove rename renameat						      \
+	remove rename renameat renameat2				      \
 	flockfile ftrylockfile funlockfile				      \
 	isoc99_scanf isoc99_vscanf isoc99_fscanf isoc99_vfscanf isoc99_sscanf \
 	isoc99_vsscanf							      \
@@ -62,6 +62,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
 	 tst-vfprintf-user-type \
 	 tst-vfprintf-mbs-prec \
 	 tst-scanf-round \
+	 tst-renameat2 \
 
 test-srcs = tst-unbputc tst-printf
 
diff --git a/stdio-common/Versions b/stdio-common/Versions
index 5016f69c20..b8217578c8 100644
--- a/stdio-common/Versions
+++ b/stdio-common/Versions
@@ -57,6 +57,9 @@ libc {
     psiginfo;
     register_printf_modifier; register_printf_type; register_printf_specifier;
   }
+  GLIBC_2.28 {
+    renameat2;
+  }
   GLIBC_PRIVATE {
     # global variables
     _itoa_lower_digits;
diff --git a/stdio-common/renameat.c b/stdio-common/renameat.c
index 2180b87bdf..98c8f1d18b 100644
--- a/stdio-common/renameat.c
+++ b/stdio-common/renameat.c
@@ -22,7 +22,7 @@
 
 /* Rename the file OLD relative to OLDFD to NEW relative to NEWFD.  */
 int
-renameat (int oldfd, const char *old, int newfd, const char *new)
+__renameat (int oldfd, const char *old, int newfd, const char *new)
 {
   if ((oldfd < 0 && oldfd != AT_FDCWD) || (newfd < 0 && newfd != AT_FDCWD))
     {
@@ -40,5 +40,6 @@ renameat (int oldfd, const char *old, int newfd, const char *new)
   return -1;
 }
 
-
+libc_hidden_def (__renameat)
+weak_alias (__renameat, renameat)
 stub_warning (renameat)
diff --git a/stdio-common/renameat2.c b/stdio-common/renameat2.c
new file mode 100644
index 0000000000..c2cedcd2cb
--- /dev/null
+++ b/stdio-common/renameat2.c
@@ -0,0 +1,30 @@
+/* Generic implementation of the renameat function.
+   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 <stdio.h>
+
+int
+renameat2 (int oldfd, const char *old, int newfd, const char *new,
+           unsigned int flags)
+{
+  if (flags == 0)
+    return __renameat (oldfd, old, newfd, new);
+  __set_errno (EINVAL);
+  return -1;
+}
diff --git a/stdio-common/tst-renameat2.c b/stdio-common/tst-renameat2.c
new file mode 100644
index 0000000000..958b0918d6
--- /dev/null
+++ b/stdio-common/tst-renameat2.c
@@ -0,0 +1,204 @@
+/* Linux implementation for renameat2 function.
+   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 <array_length.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/xunistd.h>
+#include <unistd.h>
+
+/* Directory with the temporary files.  */
+static char *directory;
+static int directory_fd;
+
+/* Paths within that directory.  */
+static char *old_path;          /* File is called "old".  */
+static char *new_path;          /* File is called "new".  */
+
+/* Subdirectory within the directory above.  */
+static char *subdirectory;
+int subdirectory_fd;
+
+/* And a pathname in that directory (called "file").  */
+static char *subdir_path;
+
+static void
+prepare (int argc, char **argv)
+{
+  directory = support_create_temp_directory ("tst-renameat2-");
+  directory_fd = xopen (directory, O_RDONLY | O_DIRECTORY, 0);
+  old_path = xasprintf ("%s/old", directory);
+  add_temp_file (old_path);
+  new_path = xasprintf ("%s/new", directory);
+  add_temp_file (new_path);
+  subdirectory = xasprintf ("%s/subdir", directory);
+  xmkdir (subdirectory, 0777);
+  add_temp_file (subdirectory);
+  subdirectory_fd = xopen (subdirectory, O_RDONLY | O_DIRECTORY, 0);
+  subdir_path = xasprintf ("%s/file", subdirectory);
+  add_temp_file (subdir_path);
+}
+
+/* Delete all files, preparing a clean slate for the next test.  */
+static void
+delete_all_files (void)
+{
+  char *files[] = { old_path, new_path, subdir_path };
+  for (size_t i = 0; i < array_length (files); ++i)
+    if (unlink (files[i]) != 0 && errno != ENOENT)
+      FAIL_EXIT1 ("unlink (\"%s\"): %m", files[i]);
+}
+
+/* Return true if PATH exists in the file system.  */
+static bool
+file_exists (const char *path)
+{
+  return access (path, F_OK) == 0;
+}
+
+/* Check that PATH exists and has size EXPECTED_SIZE.  */
+static void
+check_size (const char *path, off64_t expected_size)
+{
+  struct stat64 st;
+  xstat (path, &st);
+  if (st.st_size != expected_size)
+    FAIL_EXIT1 ("file \"%s\": expected size %lld, actual size %lld",
+                path, (unsigned long long int) expected_size,
+                (unsigned long long int) st.st_size);
+}
+
+/* Rename tests where the target does not exist.  */
+static void
+rename_without_existing_target (unsigned int flags)
+{
+  delete_all_files ();
+  support_write_file_string (old_path, "");
+  TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, flags), 0);
+  TEST_VERIFY (!file_exists (old_path));
+  TEST_VERIFY (file_exists (new_path));
+
+  delete_all_files ();
+  support_write_file_string (old_path, "");
+  TEST_COMPARE (renameat2 (directory_fd, "old", AT_FDCWD, new_path, flags), 0);
+  TEST_VERIFY (!file_exists (old_path));
+  TEST_VERIFY (file_exists (new_path));
+
+  delete_all_files ();
+  support_write_file_string (old_path, "");
+  TEST_COMPARE (renameat2 (directory_fd, "old", subdirectory_fd, "file", 0),
+                0);
+  TEST_VERIFY (!file_exists (old_path));
+  TEST_VERIFY (file_exists (subdir_path));
+}
+
+static int
+do_test (void)
+{
+  /* Tests with zero flags argument.  These are expected to succeed
+     because this renameat2 variant can be implemented with
+     renameat.  */
+  rename_without_existing_target (0);
+
+  /* renameat2 without flags replaces an existing destination.  */
+  delete_all_files ();
+  support_write_file_string (old_path, "123");
+  support_write_file_string (new_path, "1234");
+  TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, 0), 0);
+  TEST_VERIFY (!file_exists (old_path));
+  check_size (new_path, 3);
+
+  /* Now we need to check for kernel support of renameat2 with
+     flags.  */
+  delete_all_files ();
+  support_write_file_string (old_path, "");
+  if (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, RENAME_NOREPLACE)
+      != 0)
+    {
+      if (errno == EINVAL)
+        puts ("warning: no support for renameat2 with flags");
+      else
+        FAIL_EXIT1 ("renameat2 probe failed: %m");
+    }
+  else
+    {
+      /* We have full renameat2 support.  */
+      rename_without_existing_target (RENAME_NOREPLACE);
+
+      /* Now test RENAME_NOREPLACE with an existing target.  */
+      delete_all_files ();
+      support_write_file_string (old_path, "123");
+      support_write_file_string (new_path, "1234");
+      TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path,
+                               RENAME_NOREPLACE), -1);
+      TEST_COMPARE (errno, EEXIST);
+      check_size (old_path, 3);
+      check_size (new_path, 4);
+
+      delete_all_files ();
+      support_write_file_string (old_path, "123");
+      support_write_file_string (new_path, "1234");
+      TEST_COMPARE (renameat2 (directory_fd, "old", AT_FDCWD, new_path,
+                               RENAME_NOREPLACE), -1);
+      TEST_COMPARE (errno, EEXIST);
+      check_size (old_path, 3);
+      check_size (new_path, 4);
+
+      delete_all_files ();
+      support_write_file_string (old_path, "123");
+      support_write_file_string (subdir_path, "1234");
+      TEST_COMPARE (renameat2 (directory_fd, "old", subdirectory_fd, "file",
+                               RENAME_NOREPLACE), -1);
+      TEST_COMPARE (errno, EEXIST);
+      check_size (old_path, 3);
+      check_size (subdir_path, 4);
+
+      /* The flag combination of RENAME_NOREPLACE and RENAME_EXCHANGE
+         is invalid.  */
+      TEST_COMPARE (renameat2 (directory_fd, "ignored",
+                               subdirectory_fd, "ignored",
+                               RENAME_NOREPLACE | RENAME_EXCHANGE), -1);
+      TEST_COMPARE (errno, EINVAL);
+    }
+
+  /* Create all the pathnames to avoid warnings from the test
+     harness.  */
+  support_write_file_string (old_path, "");
+  support_write_file_string (new_path, "");
+  support_write_file_string (subdir_path, "");
+
+  free (directory);
+  free (subdirectory);
+  free (old_path);
+  free (new_path);
+  free (subdir_path);
+
+  xclose (directory_fd);
+  xclose (subdirectory_fd);
+
+  return 0;
+}
+
+#define PREPARE prepare
+#include <support/test-driver.c>
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index 3d46de795d..9df86e491f 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2035,6 +2035,7 @@ GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/mach/hurd/renameat.c b/sysdeps/mach/hurd/renameat.c
index 43609600d9..7985763f73 100644
--- a/sysdeps/mach/hurd/renameat.c
+++ b/sysdeps/mach/hurd/renameat.c
@@ -22,7 +22,7 @@
 
 /* Rename the file OLD relative to OLDFD to NEW relative to NEWFD.  */
 int
-renameat (int oldfd, const char *old, int newfd, const char *new)
+__renameat (int oldfd, const char *old, int newfd, const char *new)
 {
   error_t err;
   file_t olddir, newdir;
@@ -45,3 +45,5 @@ renameat (int oldfd, const char *old, int newfd, const char *new)
     return __hurd_fail (err);
   return 0;
 }
+libc_hidden_def (__renameat)
+weak_alias (__renameat, renameat)
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 884d0dfa95..7a272a19bb 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2132,3 +2132,4 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
diff --git a/sysdeps/unix/sysv/linux/alpha/kernel-features.h b/sysdeps/unix/sysv/linux/alpha/kernel-features.h
index c2d4a9f55b..5781cffd5a 100644
--- a/sysdeps/unix/sysv/linux/alpha/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/alpha/kernel-features.h
@@ -35,6 +35,11 @@
 #define __ASSUME_RECV_SYSCALL	1
 #define __ASSUME_SEND_SYSCALL	1
 
+/* Support for the renameat2 syscall was added in 3.17.  */
+#if __LINUX_KERNEL_VERSION < 0x031100
+# undef __ASSUME_RENAMEAT2
+#endif
+
 /* Support for the execveat syscall was added in 4.2.  */
 #if __LINUX_KERNEL_VERSION < 0x040200
 # undef __ASSUME_EXECVEAT
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 28d54b9794..23fec55d97 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2027,6 +2027,7 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index dfde3bd725..b203160889 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -117,6 +117,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 06b00f730a..64809ca403 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -1874,6 +1874,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 1c1cc00d40..4a87f62ad9 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2039,6 +2039,7 @@ GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index f6e17a005f..6bdd0d0443 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -1908,6 +1908,7 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
index b90ea30195..86fc66574b 100644
--- a/sysdeps/unix/sysv/linux/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/kernel-features.h
@@ -97,6 +97,11 @@
    implementation based on p{read,write}v and returning an error for
    non supported flags.  */
 
+/* Support for the renameat2 system call was added in kernel 3.15.  */
+#if __LINUX_KERNEL_VERSION >= 0x030F00
+# define __ASSUME_RENAMEAT2
+#endif
+
 /* Support for the execveat syscall was added in 3.19.  */
 #if __LINUX_KERNEL_VERSION >= 0x031300
 # define __ASSUME_EXECVEAT	1
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index ee054a618d..3226f916eb 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -118,6 +118,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0x98
 GLIBC_2.4 _IO_2_1_stdin_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 227a0581cb..b1074eeed1 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -1983,6 +1983,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/microblaze/kernel-features.h b/sysdeps/unix/sysv/linux/microblaze/kernel-features.h
index b13b863f06..7db3d05636 100644
--- a/sysdeps/unix/sysv/linux/microblaze/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/microblaze/kernel-features.h
@@ -48,6 +48,11 @@
 # undef __ASSUME_SENDMMSG_SYSCALL
 #endif
 
+/* Support for the renameat2 syscall was added in 3.17.  */
+#if __LINUX_KERNEL_VERSION < 0x031100
+# undef __ASSUME_RENAMEAT2
+#endif
+
 /* Support for the execveat syscall was added in 4.0.  */
 #if __LINUX_KERNEL_VERSION < 0x040000
 # undef __ASSUME_EXECVEAT
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index 18781b3017..b52fc7d6f7 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2124,3 +2124,4 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 2d86989cf2..718c74262a 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -1961,6 +1961,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index b8b113e1a5..9b218a9435 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -1959,6 +1959,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 6a3cd13e2d..5a90ab83e7 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -1967,6 +1967,7 @@ GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 596ec05379..3005fc9500 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -1962,6 +1962,7 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 8da18eed57..a87fbbeb6b 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2165,3 +2165,4 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 555751eb12..d56f776a52 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -1987,6 +1987,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index 80324e41aa..2b5337aab6 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -1991,6 +1991,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
index 97b1d354af..d0dfde3897 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
@@ -2222,3 +2222,4 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
index 15be314921..d505ae0e7d 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
@@ -117,6 +117,7 @@ GLIBC_2.27 wcstof32x_l F
 GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 _Exit F
 GLIBC_2.3 _IO_2_1_stderr_ D 0xe0
 GLIBC_2.3 _IO_2_1_stdin_ D 0xe0
diff --git a/sysdeps/unix/sysv/linux/renameat.c b/sysdeps/unix/sysv/linux/renameat.c
index 034432b934..f85c5ae0ec 100644
--- a/sysdeps/unix/sysv/linux/renameat.c
+++ b/sysdeps/unix/sysv/linux/renameat.c
@@ -22,7 +22,7 @@
 #include <errno.h>
 
 int
-renameat (int oldfd, const char *old, int newfd, const char *new)
+__renameat (int oldfd, const char *old, int newfd, const char *new)
 {
 #ifdef __NR_renameat
   return INLINE_SYSCALL_CALL (renameat, oldfd, old, newfd, new);
@@ -30,3 +30,5 @@ renameat (int oldfd, const char *old, int newfd, const char *new)
   return INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, 0);
 #endif
 }
+libc_hidden_def (__renameat)
+weak_alias (__renameat, renameat)
diff --git a/sysdeps/unix/sysv/linux/renameat2.c b/sysdeps/unix/sysv/linux/renameat2.c
new file mode 100644
index 0000000000..919bb2a0d4
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/renameat2.c
@@ -0,0 +1,44 @@
+/* Linux implementation for renameat2 function.
+   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 <stdio.h>
+#include <sysdep.h>
+
+int
+renameat2 (int oldfd, const char *old, int newfd, const char *new,
+           unsigned int flags)
+{
+#if !defined (__NR_renameat) || defined (__ASSUME_RENAMEAT2)
+  return INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, flags);
+#else
+  if (flags == 0)
+    return __renameat (oldfd, old, newfd, new);
+# ifdef __NR_renameat2
+  /* For non-zero flags, try the renameat2 system call.  */
+  int ret = INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, flags);
+  if (ret != -1 || errno != ENOSYS)
+    /* Preserve non-error/non-ENOSYS return values.  */
+    return ret;
+# endif
+  /* No kernel (header) support for renameat2.  All flags are
+     unknown.  */
+  __set_errno (EINVAL);
+  return -1;
+#endif
+}
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 436b992251..33f751abc0 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2094,3 +2094,4 @@ GLIBC_2.27 xencrypt F
 GLIBC_2.27 xprt_register F
 GLIBC_2.27 xprt_unregister F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index f66715f0c9..c4ec93ff43 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -1996,6 +1996,7 @@ GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index bd6242861b..2a6a0abde8 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -1901,6 +1901,7 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/sh/kernel-features.h b/sysdeps/unix/sysv/linux/sh/kernel-features.h
index b82d032e6b..05b7dcd037 100644
--- a/sysdeps/unix/sysv/linux/sh/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/sh/kernel-features.h
@@ -51,4 +51,9 @@
 /* sh only supports ipc syscall.  */
 #undef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
 
+/* Support for the renameat2 syscall was added in 4.8.  */
+#if __LINUX_KERNEL_VERSION < 0x040800
+# undef __ASSUME_RENAMEAT2
+#endif
+
 #endif
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index f2f070fbce..8de0d1711a 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -1878,6 +1878,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/sparc/kernel-features.h b/sysdeps/unix/sysv/linux/sparc/kernel-features.h
index 64d714036e..91990a716f 100644
--- a/sysdeps/unix/sysv/linux/sparc/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/sparc/kernel-features.h
@@ -41,6 +41,11 @@
 /* sparc only supports ipc syscall.  */
 #undef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
 
+/* Support for the renameat2 syscall was added in 3.16.  */
+#if __LINUX_KERNEL_VERSION < 0x031000
+# undef __ASSUME_RENAMEAT2
+#endif
+
 /* SPARC kernel Kconfig does not define CONFIG_CLONE_BACKWARDS, however it
    has the same ABI as if it did, implemented by sparc-specific code
    (sparc_do_fork).
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 265087f6a8..9858460c9f 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -1990,6 +1990,7 @@ GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 16a69812cb..a22b8fb7ca 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -1931,6 +1931,7 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index fa8c198d13..d5d71cccba 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1889,6 +1889,7 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 2536971682..e6ad42440e 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2140,3 +2140,4 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
Joseph Myers June 30, 2018, 10:26 p.m. UTC | #3
On Sat, 30 Jun 2018, Florian Weimer wrote:

> Is there any good source of information for this?  It's kind of complicated

I don't know a good source.  I examined 3.15 diffs, and then investigated 
further those glibc architectures with no architecture additions for 
renameat in 3.15.
Yury Norov July 1, 2018, 9:49 p.m. UTC | #4
Hi Florian,

On Sat, Jun 30, 2018 at 02:14:47PM +0200, Florian Weimer wrote:
> The implementation falls back to renameat if renameat2 is not available
> in the kernel (or in the kernel headers) and the flags argument is zero.
> Without kernel support, a non-zero argument returns EINVAL, not ENOSYS.
> This mirrors what the kernel does for invalid renameat2 flags.
> 
> 2018-06-30  Florian Weimer  <fweimer@redhat.com>
> 
>         [BZ # 17662]
>         * libio/stdio.h [__USE_GNU] (RENAME_NOREPLACE, RENAME_EXCHANGE)
>         (RENAME_WHITEOUT): Define.
>         [__USE_GNU] (renameat2): Declare.
>         * stdio-common/Makefile (routines): Add renameat2.
>         (tests): Add tst-renameat2.
>         * stdio-common/Versions (GLIBC_2_28): Export renameat2.
>         * stdio-common/renameat2.c: New file.
>         * stdio-common/tst-renameat2.c: Likewise.
>         * sysdeps/unix/sysv/linux/renameat2.c: Likewise.
>         * manual/filesys.texi (Temporary Files): Note that renameat2 is
>         undocumented.
>         * sysdeps/unix/sysv/linux/kernel-features.h
>         [__LINUX_KERNEL_VERSION >= 0x030F00] (__ASSUME_RENAMEAT2): Define.
>         * include/stdio.h (__renameat): Add alias for renameat.
>         * stdio-common/renameat.c (__renameat): Rename from renameat.
>         Add hidden definition and alias.
>         * sysdeps/unix/sysv/linux/renameat.c: Likewise.
>         * sysdeps/mach/hurd/renameat.c: Likewise.
>         * sysdeps/**/libc*.abilist: Add renameat2.
> 
> diff --git a/NEWS b/NEWS
> index a27dd371a3..093f364c7e 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -35,6 +35,8 @@ Major new features:
>  * Building and running on GNU/Hurd systems now works without out-of-tree
>    patches.
> 
> +* The renameat2 function has been added.
> +
>  * IDN domain names in getaddrinfo and getnameinfo now use the system libidn2
>    library if installed.  libidn2 version 2.0.5 or later is recommended.  If
>    libidn2 is not available, internationalized domain names are not encoded
> diff --git a/include/stdio.h b/include/stdio.h
> index f140813ad6..3ba0edc924 100644
> --- a/include/stdio.h
> +++ b/include/stdio.h
> @@ -237,5 +237,8 @@ __putc_unlocked (int __c, FILE *__stream)
>  }
>  #  endif
> 
> +extern __typeof (renameat) __renameat;
> +libc_hidden_proto (__renameat)
> +
>  # endif /* not _ISOMAC */
>  #endif /* stdio.h */
> diff --git a/libio/stdio.h b/libio/stdio.h
> index 731f8e56f4..3cdc8cc532 100644
> --- a/libio/stdio.h
> +++ b/libio/stdio.h
> @@ -153,6 +153,18 @@ extern int renameat (int __oldfd, const char *__old, int __newfd,
>                      const char *__new) __THROW;
>  #endif
> 
> +#ifdef __USE_GNU
> +/* Flags for renameat.  */

Flags for renameat2, right?

> +# define RENAME_NOREPLACE (1 << 0)
> +# define RENAME_EXCHANGE (1 << 1)
> +# define RENAME_WHITEOUT (1 << 2)

I really don't understand how it works. Could you / somebody explain me?

include/uapi/linux/fs.h in kernel sources already defines this flags,
and this file is usually available in Linux distribution. So I don't
understand what for it is duplicated here. If you keep in mind
old linux headers or non-linux systems, I think it should be protected
with #ifndef guards.

This is quite common though and sometimes causes real troubles.
sysdeps/unix/sysv/linux/s390/tst-ptrace-singleblock.c

> +
> +/* Rename file OLD relative to OLDFD to NEW relative to NEWFD, with
> +   additional flags.  */
> +extern int renameat2 (int __oldfd, const char *__old, int __newfd,
> +                     const char *__new, unsigned int __flags) __THROW;
> +#endif
> +
>  /* Create a temporary file and open it read/write.
> 
>     This function is a possible cancellation point and therefore not
> diff --git a/manual/filesys.texi b/manual/filesys.texi
> index cc70a6b7ee..db2f1041de 100644
> --- a/manual/filesys.texi
> +++ b/manual/filesys.texi
> @@ -3552,6 +3552,7 @@ The @code{mkdtemp} function comes from OpenBSD.
>  @c open_by_handle_at
>  @c readlinkat
>  @c renameat
> +@c renameat2
>  @c scandirat
>  @c symlinkat
>  @c unlinkat
> diff --git a/stdio-common/Makefile b/stdio-common/Makefile
> index 738a3cead0..6fd628708f 100644
> --- a/stdio-common/Makefile
> +++ b/stdio-common/Makefile
> @@ -35,7 +35,7 @@ routines      :=                                                            \
>         perror psignal                                                        \
>         tmpfile tmpfile64 tmpnam tmpnam_r tempnam tempname                    \
>         getline getw putw                                                     \
> -       remove rename renameat                                                \
> +       remove rename renameat renameat2                                      \
>         flockfile ftrylockfile funlockfile                                    \
>         isoc99_scanf isoc99_vscanf isoc99_fscanf isoc99_vfscanf isoc99_sscanf \
>         isoc99_vsscanf                                                        \
> @@ -62,6 +62,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
>          tst-vfprintf-user-type \
>          tst-vfprintf-mbs-prec \
>          tst-scanf-round \
> +        tst-renameat2 \
> 
>  test-srcs = tst-unbputc tst-printf
> 
> diff --git a/stdio-common/Versions b/stdio-common/Versions
> index 5016f69c20..b8217578c8 100644
> --- a/stdio-common/Versions
> +++ b/stdio-common/Versions
> @@ -57,6 +57,9 @@ libc {
>      psiginfo;
>      register_printf_modifier; register_printf_type; register_printf_specifier;
>    }
> +  GLIBC_2.28 {
> +    renameat2;
> +  }
>    GLIBC_PRIVATE {
>      # global variables
>      _itoa_lower_digits;
> diff --git a/stdio-common/renameat.c b/stdio-common/renameat.c
> index 2180b87bdf..98c8f1d18b 100644
> --- a/stdio-common/renameat.c
> +++ b/stdio-common/renameat.c
> @@ -22,7 +22,7 @@
> 
>  /* Rename the file OLD relative to OLDFD to NEW relative to NEWFD.  */
>  int
> -renameat (int oldfd, const char *old, int newfd, const char *new)
> +__renameat (int oldfd, const char *old, int newfd, const char *new)
>  {
>    if ((oldfd < 0 && oldfd != AT_FDCWD) || (newfd < 0 && newfd != AT_FDCWD))
>      {
> @@ -40,5 +40,6 @@ renameat (int oldfd, const char *old, int newfd, const char *new)
>    return -1;
>  }
> 
> -
> +libc_hidden_def (__renameat)
> +weak_alias (__renameat, renameat)
>  stub_warning (renameat)

This will introduce new function that will never be called if glibc is
compiled against modern headers, and linker will not be able to throw
it away. Or I miss something?

> diff --git a/stdio-common/renameat2.c b/stdio-common/renameat2.c
> new file mode 100644
> index 0000000000..c2cedcd2cb
> --- /dev/null
> +++ b/stdio-common/renameat2.c
> @@ -0,0 +1,30 @@
> +/* Generic implementation of the renameat function.
> +   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 <stdio.h>
> +
> +int
> +renameat2 (int oldfd, const char *old, int newfd, const char *new,
> +           unsigned int flags)
> +{
> +  if (flags == 0)
> +    return __renameat (oldfd, old, newfd, new);

Please, empty line after 'return ...'.

IIUC, you call __renameat here to make sanity check of arguments.
For me as user of API, it would be more important to know that my
system doesn't support syscall, than some argument is invalid (which
makes me think that syscall is supported). It may waste my time
as I will dig the problem in wrong direction.

Also, if, say, flags is 0 and oldfd < 0, the error will be EBADF;
but if flags != 0 and oldfd < 0, the error will be EINVAL. This is
definitely the mess that I'd like to avoid in system library.

Why not just drop this 2 lines?

> +  __set_errno (EINVAL);

It should be ENOSYS, I suppose.

> +  return -1;
> +}


> diff --git a/stdio-common/tst-renameat2.c b/stdio-common/tst-renameat2.c
> new file mode 100644
> index 0000000000..958b0918d6
> --- /dev/null
> +++ b/stdio-common/tst-renameat2.c
> @@ -0,0 +1,204 @@
> +/* Linux implementation for renameat2 function.
> +   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 <array_length.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <support/check.h>
> +#include <support/support.h>
> +#include <support/temp_file.h>
> +#include <support/xunistd.h>
> +#include <unistd.h>
> +
> +/* Directory with the temporary files.  */
> +static char *directory;
> +static int directory_fd;
> +
> +/* Paths within that directory.  */
> +static char *old_path;          /* File is called "old".  */
> +static char *new_path;          /* File is called "new".  */
> +
> +/* Subdirectory within the directory above.  */
> +static char *subdirectory;
> +int subdirectory_fd;
> +
> +/* And a pathname in that directory (called "file").  */
> +static char *subdir_path;
> +
> +static void
> +prepare (int argc, char **argv)
> +{
> +  directory = support_create_temp_directory ("tst-renameat2-");
> +  directory_fd = xopen (directory, O_RDONLY | O_DIRECTORY, 0);
> +  old_path = xasprintf ("%s/old", directory);
> +  add_temp_file (old_path);
> +  new_path = xasprintf ("%s/new", directory);
> +  add_temp_file (new_path);
> +  subdirectory = xasprintf ("%s/subdir", directory);
> +  xmkdir (subdirectory, 0777);
> +  add_temp_file (subdirectory);
> +  subdirectory_fd = xopen (subdirectory, O_RDONLY | O_DIRECTORY, 0);
> +  subdir_path = xasprintf ("%s/file", subdirectory);
> +  add_temp_file (subdir_path);
> +}
> +
> +/* Delete all files, preparing a clean slate for the next test.  */
> +static void
> +delete_all_files (void)
> +{
> +  char *files[] = { old_path, new_path, subdir_path };
> +  for (size_t i = 0; i < array_length (files); ++i)
> +    if (unlink (files[i]) != 0 && errno != ENOENT)
> +      FAIL_EXIT1 ("unlink (\"%s\"): %m", files[i]);
> +}
> +
> +/* Return true if PATH exists in the file system.  */
> +static bool
> +file_exists (const char *path)
> +{
> +  return access (path, F_OK) == 0;
> +}
> +
> +/* Check that PATH exists and has size EXPECTED_SIZE.  */
> +static void
> +check_size (const char *path, off64_t expected_size)
> +{
> +  struct stat64 st;
> +  xstat (path, &st);
> +  if (st.st_size != expected_size)
> +    FAIL_EXIT1 ("file \"%s\": expected size %lld, actual size %lld",
> +                path, (unsigned long long int) expected_size,
> +                (unsigned long long int) st.st_size);
> +}
> +
> +/* Rename tests where the target does not exist.  */
> +static void
> +rename_without_existing_target (unsigned int flags)
> +{
> +  delete_all_files ();
> +  support_write_file_string (old_path, "");
> +  TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, flags), 0);
> +  TEST_VERIFY (!file_exists (old_path));
> +  TEST_VERIFY (file_exists (new_path));
> +
> +  delete_all_files ();
> +  support_write_file_string (old_path, "");
> +  TEST_COMPARE (renameat2 (directory_fd, "old", AT_FDCWD, new_path, flags), 0);
> +  TEST_VERIFY (!file_exists (old_path));
> +  TEST_VERIFY (file_exists (new_path));
> +
> +  delete_all_files ();
> +  support_write_file_string (old_path, "");
> +  TEST_COMPARE (renameat2 (directory_fd, "old", subdirectory_fd, "file", 0),
> +                0);
> +  TEST_VERIFY (!file_exists (old_path));
> +  TEST_VERIFY (file_exists (subdir_path));
> +}
> +
> +static int
> +do_test (void)
> +{
> +  /* Tests with zero flags argument.  These are expected to succeed
> +     because this renameat2 variant can be implemented with
> +     renameat.  */
> +  rename_without_existing_target (0);
> +
> +  /* renameat2 without flags replaces an existing destination.  */
> +  delete_all_files ();
> +  support_write_file_string (old_path, "123");
> +  support_write_file_string (new_path, "1234");
> +  TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, 0), 0);
> +  TEST_VERIFY (!file_exists (old_path));
> +  check_size (new_path, 3);
> +
> +  /* Now we need to check for kernel support of renameat2 with
> +     flags.  */
> +  delete_all_files ();
> +  support_write_file_string (old_path, "");
> +  if (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, RENAME_NOREPLACE)
> +      != 0)
> +    {
> +      if (errno == EINVAL)
> +        puts ("warning: no support for renameat2 with flags");

This is wrong. There's no "renameat2 without flags".

According to man page, for renameat2 EINVAL means one of:
EINVAL An invalid flag was specified in flags.
EINVAL Both RENAME_NOREPLACE and RENAME_EXCHANGE were specified in flags.
EINVAL Both RENAME_WHITEOUT and RENAME_EXCHANGE were specified in flags.
EINVAL The filesystem does not support one of the flags in flags.

> +      else
> +        FAIL_EXIT1 ("renameat2 probe failed: %m");
> +    }
> +  else
> +    {
> +      /* We have full renameat2 support.  */
> +      rename_without_existing_target (RENAME_NOREPLACE);
> +
> +      /* Now test RENAME_NOREPLACE with an existing target.  */
> +      delete_all_files ();
> +      support_write_file_string (old_path, "123");
> +      support_write_file_string (new_path, "1234");
> +      TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path,
> +                               RENAME_NOREPLACE), -1);
> +      TEST_COMPARE (errno, EEXIST);
> +      check_size (old_path, 3);
> +      check_size (new_path, 4);
> +
> +      delete_all_files ();
> +      support_write_file_string (old_path, "123");
> +      support_write_file_string (new_path, "1234");
> +      TEST_COMPARE (renameat2 (directory_fd, "old", AT_FDCWD, new_path,
> +                               RENAME_NOREPLACE), -1);
> +      TEST_COMPARE (errno, EEXIST);
> +      check_size (old_path, 3);
> +      check_size (new_path, 4);
> +
> +      delete_all_files ();
> +      support_write_file_string (old_path, "123");
> +      support_write_file_string (subdir_path, "1234");
> +      TEST_COMPARE (renameat2 (directory_fd, "old", subdirectory_fd, "file",
> +                               RENAME_NOREPLACE), -1);
> +      TEST_COMPARE (errno, EEXIST);
> +      check_size (old_path, 3);
> +      check_size (subdir_path, 4);
> +
> +      /* The flag combination of RENAME_NOREPLACE and RENAME_EXCHANGE
> +         is invalid.  */
> +      TEST_COMPARE (renameat2 (directory_fd, "ignored",
> +                               subdirectory_fd, "ignored",
> +                               RENAME_NOREPLACE | RENAME_EXCHANGE), -1);
> +      TEST_COMPARE (errno, EINVAL);
> +    }
> +
> +  /* Create all the pathnames to avoid warnings from the test
> +     harness.  */
> +  support_write_file_string (old_path, "");
> +  support_write_file_string (new_path, "");
> +  support_write_file_string (subdir_path, "");
> +
> +  free (directory);
> +  free (subdirectory);
> +  free (old_path);
> +  free (new_path);
> +  free (subdir_path);
> +
> +  xclose (directory_fd);
> +  xclose (subdirectory_fd);
> +
> +  return 0;
> +}
> +
> +#define PREPARE prepare
> +#include <support/test-driver.c>

[...]

> diff --git a/sysdeps/unix/sysv/linux/renameat.c b/sysdeps/unix/sysv/linux/renameat.c
> index 034432b934..f85c5ae0ec 100644
> --- a/sysdeps/unix/sysv/linux/renameat.c
> +++ b/sysdeps/unix/sysv/linux/renameat.c
> @@ -22,7 +22,7 @@
>  #include <errno.h>
> 
>  int
> -renameat (int oldfd, const char *old, int newfd, const char *new)
> +__renameat (int oldfd, const char *old, int newfd, const char *new)
>  {
>  #ifdef __NR_renameat
>    return INLINE_SYSCALL_CALL (renameat, oldfd, old, newfd, new);
> @@ -30,3 +30,5 @@ renameat (int oldfd, const char *old, int newfd, const char *new)
>    return INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, 0);
>  #endif
>  }
> +libc_hidden_def (__renameat)
> +weak_alias (__renameat, renameat)
> diff --git a/sysdeps/unix/sysv/linux/renameat2.c b/sysdeps/unix/sysv/linux/renameat2.c
> new file mode 100644
> index 0000000000..919bb2a0d4
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/renameat2.c
> @@ -0,0 +1,44 @@
> +/* Linux implementation for renameat2 function.
> +   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 <stdio.h>
> +#include <sysdep.h>
> +
> +int
> +renameat2 (int oldfd, const char *old, int newfd, const char *new,
> +           unsigned int flags)
> +{
> +#if !defined (__NR_renameat) || defined (__ASSUME_RENAMEAT2)
> +  return INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, flags);
> +#else
> +  if (flags == 0)
> +    return __renameat (oldfd, old, newfd, new);

What for this special case? The most-conservative-strategy argument
doesn't work here because this is new syscall, and user really wants
renameat2(), if he calls it explicitly in his new code.

> +# ifdef __NR_renameat2
> +  /* For non-zero flags, try the renameat2 system call.  */
> +  int ret = INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, flags);
> +  if (ret != -1 || errno != ENOSYS)
> +    /* Preserve non-error/non-ENOSYS return values.  */
> +    return ret;

You can drop if (...), just return INLINE_SYSCALL_CALL(...).

> +# endif
> +  /* No kernel (header) support for renameat2.  All flags are
> +     unknown.  */
> +  __set_errno (EINVAL);

You really need ENOSYS here.

> +  return -1;
> +#endif
> +}
  
I think this function should look like this (not tested):

int
renameat2 (int oldfd, const char *old, int newfd, const char *new,
           unsigned int flags)
{
# ifdef __NR_renameat2
  return INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, flags);
# else
  /* Try renameat if possible.  */
  if (flags == 0)
          return INLINE_SYSCALL_CALL (renameat, oldfd, old, newfd, new);

  __set_errno (ENOSYS);
  return -1;
# endif
}

Yury
Florian Weimer July 2, 2018, 6:48 a.m. UTC | #5
On 07/01/2018 11:49 PM, Yury Norov wrote:

>> +#ifdef __USE_GNU
>> +/* Flags for renameat.  */
> 
> Flags for renameat2, right?

Thanks, fixed.

>> +# define RENAME_NOREPLACE (1 << 0)
>> +# define RENAME_EXCHANGE (1 << 1)
>> +# define RENAME_WHITEOUT (1 << 2)
> 
> I really don't understand how it works. Could you / somebody explain me?
> 
> include/uapi/linux/fs.h in kernel sources already defines this flags,
> and this file is usually available in Linux distribution. So I don't
> understand what for it is duplicated here. If you keep in mind
> old linux headers or non-linux systems, I think it should be protected
> with #ifndef guards.

<linux/fs.h> undefines and defines macros not mentioned in the standards 
(and it even contains a few unrelated structs), so we cannot include it 
without _GNU_SOURCE.

It might be possible to include it only for _GNU_SOURCE, but there are a 
lot of things in <linux/fs.h>, so that does not seem to be particularly 
advisable.

We still support building glibc with 3.2 kernel headers, and if the 
definitions you quoted above are not available, building the test case 
would fail.

>> diff --git a/stdio-common/renameat.c b/stdio-common/renameat.c
>> index 2180b87bdf..98c8f1d18b 100644
>> --- a/stdio-common/renameat.c
>> +++ b/stdio-common/renameat.c
>> @@ -22,7 +22,7 @@
>>
>>   /* Rename the file OLD relative to OLDFD to NEW relative to NEWFD.  */
>>   int
>> -renameat (int oldfd, const char *old, int newfd, const char *new)
>> +__renameat (int oldfd, const char *old, int newfd, const char *new)
>>   {
>>     if ((oldfd < 0 && oldfd != AT_FDCWD) || (newfd < 0 && newfd != AT_FDCWD))
>>       {
>> @@ -40,5 +40,6 @@ renameat (int oldfd, const char *old, int newfd, const char *new)
>>     return -1;
>>   }
>>
>> -
>> +libc_hidden_def (__renameat)
>> +weak_alias (__renameat, renameat)
>>   stub_warning (renameat)
> 
> This will introduce new function that will never be called if glibc is
> compiled against modern headers, and linker will not be able to throw
> it away. Or I miss something?

It will never be called on Linux or Hurd, no matter what the header 
versions are.  We have a policy that syscall wrappers need fallback 
definitions, and the fallback definitions are often never used (if both 
Hurd and Linux have implementations).

>> +int
>> +renameat2 (int oldfd, const char *old, int newfd, const char *new,
>> +           unsigned int flags)
>> +{
>> +  if (flags == 0)
>> +    return __renameat (oldfd, old, newfd, new);

> IIUC, you call __renameat here to make sanity check of arguments.
> For me as user of API, it would be more important to know that my
> system doesn't support syscall, than some argument is invalid (which
> makes me think that syscall is supported). It may waste my time
> as I will dig the problem in wrong direction.

No, the line above calls the Hurd or Linux renameat implementation, 
which are fully functional.

>> +  __set_errno (EINVAL);
> 
> It should be ENOSYS, I suppose.

The kernel returns EINVAL for invalid flags.  If renameat2 is not 
implemented, all flags are invalid.

>> +  /* Now we need to check for kernel support of renameat2 with
>> +     flags.  */
>> +  delete_all_files ();
>> +  support_write_file_string (old_path, "");
>> +  if (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, RENAME_NOREPLACE)
>> +      != 0)
>> +    {
>> +      if (errno == EINVAL)
>> +        puts ("warning: no support for renameat2 with flags");
> 
> This is wrong. There's no "renameat2 without flags".

I can change it to “renameat2 with non-zero flags”.

>> +int
>> +renameat2 (int oldfd, const char *old, int newfd, const char *new,
>> +           unsigned int flags)
>> +{
>> +#if !defined (__NR_renameat) || defined (__ASSUME_RENAMEAT2)
>> +  return INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, flags);
>> +#else
>> +  if (flags == 0)
>> +    return __renameat (oldfd, old, newfd, new);
> 
> What for this special case? The most-conservative-strategy argument
> doesn't work here because this is new syscall, and user really wants
> renameat2(), if he calls it explicitly in his new code.

That is not clear to me.  We do not cause renameat to fail if the system 
does not have the system call, but implements renameat2.  Why shouldn't 
we do the same thing for renameat2?

Thanks,
Florian
Yury Norov July 2, 2018, 8:46 a.m. UTC | #6
+ Alexander Viro <viro@zeniv.linux.org.uk>, kernel maillists. 

On Mon, Jul 02, 2018 at 08:48:36AM +0200, Florian Weimer wrote:
> On 07/01/2018 11:49 PM, Yury Norov wrote:
> 
> > > +#ifdef __USE_GNU
> > > +/* Flags for renameat.  */
> > 
> > Flags for renameat2, right?
> 
> Thanks, fixed.
> 
> > > +# define RENAME_NOREPLACE (1 << 0)
> > > +# define RENAME_EXCHANGE (1 << 1)
> > > +# define RENAME_WHITEOUT (1 << 2)
> > 
> > I really don't understand how it works. Could you / somebody explain me?
> > 
> > include/uapi/linux/fs.h in kernel sources already defines this flags,
> > and this file is usually available in Linux distribution. So I don't
> > understand what for it is duplicated here. If you keep in mind
> > old linux headers or non-linux systems, I think it should be protected
> > with #ifndef guards.
> 
> <linux/fs.h> undefines and defines macros not mentioned in the standards
> (and it even contains a few unrelated structs), so we cannot include it
> without _GNU_SOURCE.
> 
> It might be possible to include it only for _GNU_SOURCE, but there are a
> lot of things in <linux/fs.h>, so that does not seem to be particularly
> advisable.
> 
> We still support building glibc with 3.2 kernel headers, and if the
> definitions you quoted above are not available, building the test case
> would fail.

Is my understanding correct that glibc community finds <linux/fs.h>
inappropriate for their use, and prefer to re-introduce (duplicate)
its functionality locally? I think it's wrong. The right way to go
is to make kernel headers comfortable for users instead of ignoring
it.

Are you OK to switch to kernel RENAME_* definitions if they will be
located in separated small file? Like in the patch below.

Signed-off-by: Yury Norov <ynorov@caviumnetworks.com>
---
 include/uapi/linux/fs.h     |  4 +---
 include/uapi/linux/rename.h | 12 ++++++++++++
 2 files changed, 13 insertions(+), 3 deletions(-)
 create mode 100644 include/uapi/linux/rename.h

diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index c27576d471c2..46c03ea31a76 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -44,9 +44,7 @@
 #define SEEK_HOLE	4	/* seek to the next hole */
 #define SEEK_MAX	SEEK_HOLE
 
-#define RENAME_NOREPLACE	(1 << 0)	/* Don't overwrite target */
-#define RENAME_EXCHANGE		(1 << 1)	/* Exchange source and dest */
-#define RENAME_WHITEOUT		(1 << 2)	/* Whiteout source */
+#include <linux/rename.h>
 
 struct file_clone_range {
 	__s64 src_fd;
diff --git a/include/uapi/linux/rename.h b/include/uapi/linux/rename.h
new file mode 100644
index 000000000000..7178f0565657
--- /dev/null
+++ b/include/uapi/linux/rename.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI_LINUX_RENAME_H
+#define _UAPI_LINUX_RENAME_H
+
+/*
+ * Definitions for rename syscall family.
+ */
+#define RENAME_NOREPLACE	(1 << 0)	/* Don't overwrite target */
+#define RENAME_EXCHANGE		(1 << 1)	/* Exchange source and dest */
+#define RENAME_WHITEOUT		(1 << 2)	/* Whiteout source */
+
+#endif /* _UAPI_LINUX_RENAME_H */
Andreas Schwab July 2, 2018, 8:58 a.m. UTC | #7
On Jul 02 2018, Yury Norov <ynorov@caviumnetworks.com> wrote:

> include/uapi/linux/fs.h in kernel sources already defines this flags,
> and this file is usually available in Linux distribution. So I don't
> understand what for it is duplicated here.

Most of the kernel headers are not namespace clean, so they cannot be
used directly.

Andreas.
Florian Weimer July 2, 2018, 9:32 a.m. UTC | #8
On 07/02/2018 10:46 AM, Yury Norov wrote:

> Is my understanding correct that glibc community finds <linux/fs.h>
> inappropriate for their use, and prefer to re-introduce (duplicate)
> its functionality locally? I think it's wrong. The right way to go
> is to make kernel headers comfortable for users instead of ignoring
> it.

In some cases, we already use UAPI headers (<linux/falloc.h> is an 
example), but it is not always possible.

> Are you OK to switch to kernel RENAME_* definitions if they will be
> located in separated small file? Like in the patch below.
> 
> Signed-off-by: Yury Norov <ynorov@caviumnetworks.com>
> ---
>   include/uapi/linux/fs.h     |  4 +---
>   include/uapi/linux/rename.h | 12 ++++++++++++
>   2 files changed, 13 insertions(+), 3 deletions(-)
>   create mode 100644 include/uapi/linux/rename.h
> 
> diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
> index c27576d471c2..46c03ea31a76 100644
> --- a/include/uapi/linux/fs.h
> +++ b/include/uapi/linux/fs.h
> @@ -44,9 +44,7 @@
>   #define SEEK_HOLE	4	/* seek to the next hole */
>   #define SEEK_MAX	SEEK_HOLE
>   
> -#define RENAME_NOREPLACE	(1 << 0)	/* Don't overwrite target */
> -#define RENAME_EXCHANGE		(1 << 1)	/* Exchange source and dest */
> -#define RENAME_WHITEOUT		(1 << 2)	/* Whiteout source */
> +#include <linux/rename.h>
>   
>   struct file_clone_range {
>   	__s64 src_fd;
> diff --git a/include/uapi/linux/rename.h b/include/uapi/linux/rename.h
> new file mode 100644
> index 000000000000..7178f0565657
> --- /dev/null
> +++ b/include/uapi/linux/rename.h
> @@ -0,0 +1,12 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +#ifndef _UAPI_LINUX_RENAME_H
> +#define _UAPI_LINUX_RENAME_H
> +
> +/*
> + * Definitions for rename syscall family.
> + */
> +#define RENAME_NOREPLACE	(1 << 0)	/* Don't overwrite target */
> +#define RENAME_EXCHANGE		(1 << 1)	/* Exchange source and dest */
> +#define RENAME_WHITEOUT		(1 << 2)	/* Whiteout source */
> +
> +#endif /* _UAPI_LINUX_RENAME_H */

This would help.

We would need to provide definitions for compatibility with older kernel 
headers locally, but on newer kernels, we could use the UAPI header file.

Thanks,
Florian
Joseph Myers July 2, 2018, 3:11 p.m. UTC | #9
On Mon, 2 Jul 2018, Yury Norov wrote:

> I think this function should look like this (not tested):
> 
> int
> renameat2 (int oldfd, const char *old, int newfd, const char *new,
>            unsigned int flags)
> {
> # ifdef __NR_renameat2
>   return INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, flags);
> # else
>   /* Try renameat if possible.  */

That's obviously wrong; it would mean that if you start building glibc 
with newer kernel headers, it would not support a fallback, on older 
kernels used at runtime, that is supported when building with older kernel 
headers.  It is always the case that building with newer kernel headers 
should not regress support for older kernels at runtime, and that if you 
have a fallback, it's __ASSUME_*, defined based on the --enable-kernel 
version, that is used to decide whether the fallback can be disabled.  
(Whether you should have a fallback at all is a case-by-case question 
depending on whether some or all cases of the API can be effectively 
emulated on older kernels.  But the starting point for any API, certainly 
any API that is added to the OS-independent GNU API, is that the semantics 
of that API may be implemented in any convenient way, not necessarily by 
calling a directly corresponding syscall.  See e.g. how all the *at 
functions originally had fallbacks using /proc until the minimum kernel 
version for glibc was recent enough to assume the syscalls to be present.)
Paul Eggert July 2, 2018, 5:38 p.m. UTC | #10
Florian Weimer wrote:
> Without kernel support, a non-zero argument returns EINVAL, not ENOSYS.
> This mirrors what the kernel does for invalid renameat2 flags.

The Gnulib renameat2 function 
<https://www.gnu.org/software/gnulib/MODULES.html#module=renameat2> has 
different semantics with non-zero flags. On GNU/Linux if flags==RENAME_NOREPLACE 
and the Linux syscall fails due to EINVAL/ENOSYS/ENOTSUP, Gnulib renameat2 falls 
back on fstatatting the destination, failing if fstatat succeeds, and using 
ordinary renameat otherwise. Of course this implementation has a race condition, 
but Gnulib-using applications like GNU 'mv' prefer this implementation since if 
the kernel doesn't support RENAME_NOREPLACE they'd just fall back on fstatat 
themselves anyway, if renameat2 didn't do that for them.

It strikes me that this will be a common use case for RENAME_NOREPLACE in other 
glibc applications too, perhaps the most common one. In that case, perhaps glibc 
should support the Gnulib semantics, by falling back on a non-atomic 
implementation of RENAME_NOREPLACE if the kernel doesn't support the atomic one. 
If that's too drastic, how about having glibc support a new flag 
RENAME_NOREPLACE_NONATOMIC that works on all platforms? We could of course add 
such a flag to Gnulib, but I expect it'd be better if the functionality were 
available to all Glibc programs, not just to Gnulib-using programs.

PS. In Gnulib-using apps we've found no need for RENAME_EXCHANGE or 
RENAME_WHITEOUT and so Gnulib does not implement them on older GNU/Linux 
kernels. Gnulib renameat2 does support RENAME_EXCHANGE on macOS since it's easy 
there.

[CC:ing this to bug-gnulib; for those joining in, a recent email in this thread 
is here:

https://sourceware.org/ml/libc-alpha/2018-07/msg00003.html

]
Joseph Myers July 2, 2018, 7:46 p.m. UTC | #11
On Mon, 2 Jul 2018, Paul Eggert wrote:

> The Gnulib renameat2 function
> <https://www.gnu.org/software/gnulib/MODULES.html#module=renameat2> has
> different semantics with non-zero flags. On GNU/Linux if
> flags==RENAME_NOREPLACE and the Linux syscall fails due to
> EINVAL/ENOSYS/ENOTSUP, Gnulib renameat2 falls back on fstatatting the
> destination, failing if fstatat succeeds, and using ordinary renameat
> otherwise. Of course this implementation has a race condition, but
> Gnulib-using applications like GNU 'mv' prefer this implementation since if
> the kernel doesn't support RENAME_NOREPLACE they'd just fall back on fstatat
> themselves anyway, if renameat2 didn't do that for them.

We've had complaints in glibc about fallbacks that introduce races (e.g. 
<https://sourceware.org/bugzilla/show_bug.cgi?id=9813> for pselect).  Do 
you have reason to believe that this race is somehow different and 
introducing it will never cause problems for users?

There are other possible fallbacks an application could try depending on 
the files it's trying to rename and what failure conditions it wants to 
allow for - for example, a linkat/unlinkat pair, when renaming a 
non-directory that's not at the maximum link count, would also guarantee 
not to replace an existing destination, at the risk of leaving two links 
if the source directory is read-only, the destination directory is 
writable, and the destination directory is made read-only during the 
renameat2 execution before it tries to remove the destination link after 
removing the source link failed.
Paul Eggert July 2, 2018, 7:58 p.m. UTC | #12
Joseph Myers wrote:

> We've had complaints in glibc about fallbacks that introduce races (e.g.
> <https://sourceware.org/bugzilla/show_bug.cgi?id=9813> for pselect).  Do
> you have reason to believe that this race is somehow different and
> introducing it will never cause problems for users?

"Never" is a pretty strong word, and I don't know that it's true here. I do know 
that the uses of Gnulib renameat2 don't have any problems with races that they 
wouldn't have with their own fallbacks, and I haven't yet run into a potential 
case that would differ from this.

If the requirement is "never", then let's go with another flag 
(RENAME_NONATOMIC, say), that can be ORed in to allow renameat2 to behave 
non-atomically. This flag would not be passed to the Linux kernel syscall; it 
would affect only the user-mode fallback code.
Florian Weimer July 3, 2018, 6:40 a.m. UTC | #13
On 07/02/2018 07:38 PM, Paul Eggert wrote:
> Florian Weimer wrote:
>> Without kernel support, a non-zero argument returns EINVAL, not ENOSYS.
>> This mirrors what the kernel does for invalid renameat2 flags.
> 
> The Gnulib renameat2 function 
> <https://www.gnu.org/software/gnulib/MODULES.html#module=renameat2> has 
> different semantics with non-zero flags. On GNU/Linux if 
> flags==RENAME_NOREPLACE and the Linux syscall fails due to 
> EINVAL/ENOSYS/ENOTSUP, Gnulib renameat2 falls back on fstatatting the 
> destination, failing if fstatat succeeds, and using ordinary renameat 
> otherwise. Of course this implementation has a race condition, but 
> Gnulib-using applications like GNU 'mv' prefer this implementation since 
> if the kernel doesn't support RENAME_NOREPLACE they'd just fall back on 
> fstatat themselves anyway, if renameat2 didn't do that for them.

Surely that's a gnulib bug because the main reason for the 
RENAME_NOREPLACE variant renameat2 was to avoid exactly that race (or 
the other race where the file exists under both the old and new path).

The gnulib function should simply be called something else, not 
renameat2.  The present situation is unfortunate, but I don't think it 
would be an improvement if glibc copies the buggy gnulib behavior.

Thanks,
Florian
Paul Eggert July 3, 2018, 7:05 p.m. UTC | #14
Florian Weimer wrote:
> Surely that's a gnulib bug because the main reason for the RENAME_NOREPLACE 
> variant renameat2 was to avoid exactly that race (or the other race where the 
> file exists under both the old and new path).

No, it's intended behavior, not a bug. GNU mv uses renameat2 with 
RENAME_NOREPLACE. mv wants the noreplace semantics on platforms that support it 
(currently only recent Linux and macOS kernels); otherwise it wants exactly that 
race because that's the best that can be done on other platforms. If Gnulib 
renameat2 simply failed with EINVAL because RENAME_NOREPLACE was not supported, 
GNU mv would simply use the same racy fallback that Gnulib renameat2 already uses.

Other GNU applications are similar to GNU mv in this respect.

> The gnulib function should simply be called something else, not renameat2.

Although Gnulib will do that if necessary, it'll be unfortunate if GNU 
applications typically don't call glibc renameat2 directly (because it will be 
such a pain to use in the typical case) and use the Gnulib function instead. 
It'd be nicer if Glibc supported GNU applications rather than fought with them, 
and the RENAME_NONATOMIC flag I suggested would provide a more convenient and 
logical way to do that than a newly-named Gnulib function would.
Andreas Schwab July 4, 2018, 9:04 a.m. UTC | #15
On Jul 03 2018, Paul Eggert <eggert@cs.ucla.edu> wrote:

> Florian Weimer wrote:
>> Surely that's a gnulib bug because the main reason for the
>> RENAME_NOREPLACE variant renameat2 was to avoid exactly that race (or
>> the other race where the file exists under both the old and new path).
>
> No, it's intended behavior, not a bug. GNU mv uses renameat2 with
> RENAME_NOREPLACE. mv wants the noreplace semantics on platforms that
> support it (currently only recent Linux and macOS kernels); otherwise it
> wants exactly that race because that's the best that can be done on other
> platforms. If Gnulib renameat2 simply failed with EINVAL because
> RENAME_NOREPLACE was not supported, GNU mv would simply use the same racy
> fallback that Gnulib renameat2 already uses.
>
> Other GNU applications are similar to GNU mv in this respect.

IMHO we should not repeat the pselect error.  Glibc should provide the
race-free guarantee that RENAME_NOREPLACE gives, so that programs that
need it can use it without fear.

Andreas.
Florian Weimer July 4, 2018, 10:39 a.m. UTC | #16
On 07/04/2018 11:04 AM, Andreas Schwab wrote:
> On Jul 03 2018, Paul Eggert <eggert@cs.ucla.edu> wrote:
> 
>> Florian Weimer wrote:
>>> Surely that's a gnulib bug because the main reason for the
>>> RENAME_NOREPLACE variant renameat2 was to avoid exactly that race (or
>>> the other race where the file exists under both the old and new path).
>>
>> No, it's intended behavior, not a bug. GNU mv uses renameat2 with
>> RENAME_NOREPLACE. mv wants the noreplace semantics on platforms that
>> support it (currently only recent Linux and macOS kernels); otherwise it
>> wants exactly that race because that's the best that can be done on other
>> platforms. If Gnulib renameat2 simply failed with EINVAL because
>> RENAME_NOREPLACE was not supported, GNU mv would simply use the same racy
>> fallback that Gnulib renameat2 already uses.
>>
>> Other GNU applications are similar to GNU mv in this respect.
> 
> IMHO we should not repeat the pselect error.  Glibc should provide the
> race-free guarantee that RENAME_NOREPLACE gives, so that programs that
> need it can use it without fear.

What do you think about the rest of the patch?  Do you think it can be 
committed?

Thanks,
Florian
Carlos O'Donell July 4, 2018, 4:31 p.m. UTC | #17
On 07/04/2018 05:04 AM, Andreas Schwab wrote:
> On Jul 03 2018, Paul Eggert <eggert@cs.ucla.edu> wrote:
> 
>> Florian Weimer wrote:
>>> Surely that's a gnulib bug because the main reason for the
>>> RENAME_NOREPLACE variant renameat2 was to avoid exactly that race (or
>>> the other race where the file exists under both the old and new path).
>>
>> No, it's intended behavior, not a bug. GNU mv uses renameat2 with
>> RENAME_NOREPLACE. mv wants the noreplace semantics on platforms that
>> support it (currently only recent Linux and macOS kernels); otherwise it
>> wants exactly that race because that's the best that can be done on other
>> platforms. If Gnulib renameat2 simply failed with EINVAL because
>> RENAME_NOREPLACE was not supported, GNU mv would simply use the same racy
>> fallback that Gnulib renameat2 already uses.
>>
>> Other GNU applications are similar to GNU mv in this respect.
> 
> IMHO we should not repeat the pselect error.  Glibc should provide the
> race-free guarantee that RENAME_NOREPLACE gives, so that programs that
> need it can use it without fear.

I agree completely. We are not "fighting" against GNU applications, what
we are doing is providing a set of reliable semantics.

The API should be split into 2, one symbol which provides reliable race-free
semantics, and another which doesn't. Application authors should make the
choice at the source level. In this case renameat2 is the reliable race-free
name for the operation. If we really need another non-race-free API then
gnulib can provide that.
Carlos O'Donell July 4, 2018, 7:21 p.m. UTC | #18
On 06/30/2018 08:14 AM, Florian Weimer wrote:
> The implementation falls back to renameat if renameat2 is not available
> in the kernel (or in the kernel headers) and the flags argument is zero.
> Without kernel support, a non-zero argument returns EINVAL, not ENOSYS.
> This mirrors what the kernel does for invalid renameat2 flags.

This looks good to me. I object to adding emulation to this function.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>

> 2018-06-30  Florian Weimer  <fweimer@redhat.com>
> 
> 	[BZ # 17662]
> 	* libio/stdio.h [__USE_GNU] (RENAME_NOREPLACE, RENAME_EXCHANGE)
> 	(RENAME_WHITEOUT): Define.
> 	[__USE_GNU] (renameat2): Declare.
> 	* stdio-common/Makefile (routines): Add renameat2.
> 	(tests): Add tst-renameat2.
> 	* stdio-common/Versions (GLIBC_2_28): Export renameat2.
> 	* stdio-common/renameat2.c: New file.
> 	* stdio-common/tst-renameat2.c: Likewise.
> 	* sysdeps/unix/sysv/linux/renameat2.c: Likewise.
> 	* manual/filesys.texi (Temporary Files): Note that renameat2 is
> 	undocumented.
> 	* sysdeps/unix/sysv/linux/kernel-features.h
> 	[__LINUX_KERNEL_VERSION >= 0x030F00] (__ASSUME_RENAMEAT2): Define.
> 	* include/stdio.h (__renameat): Add alias for renameat.
> 	* stdio-common/renameat.c (__renameat): Rename from renameat.
> 	Add hidden definition and alias.
> 	* sysdeps/unix/sysv/linux/renameat.c: Likewise.
> 	* sysdeps/mach/hurd/renameat.c: Likewise.
> 	* sysdeps/**/libc*.abilist: Add renameat2.
> 
> diff --git a/NEWS b/NEWS
> index a27dd371a3..093f364c7e 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -35,6 +35,8 @@ Major new features:
>  * Building and running on GNU/Hurd systems now works without out-of-tree
>    patches.
>  
> +* The renameat2 function has been added.

We have a spotty track record of making these message meaningful.

Suggest something like this:

* The library now implements the renameat2 function. On Linux systems that
  support the renameat2 system call this is a wrapper for the kernel support
  and avoids the race conditions present with renameat. No emulation is provide
  and if renameat2 is not supported by the underlying operating system either
  an errno of ENOSYS or EINVAL error will be returned.

> +
>  * IDN domain names in getaddrinfo and getnameinfo now use the system libidn2
>    library if installed.  libidn2 version 2.0.5 or later is recommended.  If
>    libidn2 is not available, internationalized domain names are not encoded
> diff --git a/include/stdio.h b/include/stdio.h
> index f140813ad6..3ba0edc924 100644
> --- a/include/stdio.h
> +++ b/include/stdio.h
> @@ -237,5 +237,8 @@ __putc_unlocked (int __c, FILE *__stream)
>  }
>  #  endif
>  
> +extern __typeof (renameat) __renameat;
> +libc_hidden_proto (__renameat)
> +
>  # endif /* not _ISOMAC */
>  #endif /

OK.

* stdio.h */
> diff --git a/libio/stdio.h b/libio/stdio.h
> index 731f8e56f4..3cdc8cc532 100644
> --- a/libio/stdio.h
> +++ b/libio/stdio.h
> @@ -153,6 +153,18 @@ extern int renameat (int __oldfd, const char *__old, int __newfd,
>  		     const char *__new) __THROW;
>  #endif
>  
> +#ifdef __USE_GNU
> +/* Flags for renameat.  */

Already pointed out that this should be 'renameat2'.

> +# define RENAME_NOREPLACE (1 << 0)
> +# define RENAME_EXCHANGE (1 << 1)
> +# define RENAME_WHITEOUT (1 << 2)
> +
> +/* Rename file OLD relative to OLDFD to NEW relative to NEWFD, with
> +   additional flags.  */
> +extern int renameat2 (int __oldfd, const char *__old, int __newfd,
> +		      const char *__new, unsigned int __flags) __THROW;
> +#endif
> +
>  /* Create a temporary file and open it read/write.
>  
>     This function is a possible cancellation point and therefore not
> diff --git a/manual/filesys.texi b/manual/filesys.texi
> index cc70a6b7ee..db2f1041de 100644
> --- a/manual/filesys.texi
> +++ b/manual/filesys.texi
> @@ -3552,6 +3552,7 @@ The @code{mkdtemp} function comes from OpenBSD.
>  @c open_by_handle_at
>  @c readlinkat
>  @c renameat
> +@c renameat2
>  @c scandirat
>  @c symlinkat
>  @c unlinkat
> diff --git a/stdio-common/Makefile b/stdio-common/Makefile
> index 738a3cead0..6fd628708f 100644
> --- a/stdio-common/Makefile
> +++ b/stdio-common/Makefile
> @@ -35,7 +35,7 @@ routines	:=							      \
>  	perror psignal							      \
>  	tmpfile tmpfile64 tmpnam tmpnam_r tempnam tempname		      \
>  	getline getw putw						      \
> -	remove rename renameat						      \
> +	remove rename renameat renameat2				      \
>  	flockfile ftrylockfile funlockfile				      \
>  	isoc99_scanf isoc99_vscanf isoc99_fscanf isoc99_vfscanf isoc99_sscanf \
>  	isoc99_vsscanf							      \
> @@ -62,6 +62,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
>  	 tst-vfprintf-user-type \
>  	 tst-vfprintf-mbs-prec \
>  	 tst-scanf-round \
> +	 tst-renameat2 \
>  
>  test-srcs = tst-unbputc tst-printf
>  
> diff --git a/stdio-common/Versions b/stdio-common/Versions
> index 5016f69c20..b8217578c8 100644
> --- a/stdio-common/Versions
> +++ b/stdio-common/Versions
> @@ -57,6 +57,9 @@ libc {
>      psiginfo;
>      register_printf_modifier; register_printf_type; register_printf_specifier;
>    }
> +  GLIBC_2.28 {
> +    renameat2;
> +  }

OK.

>    GLIBC_PRIVATE {
>      # global variables
>      _itoa_lower_digits;
> diff --git a/stdio-common/renameat.c b/stdio-common/renameat.c
> index 2180b87bdf..98c8f1d18b 100644
> --- a/stdio-common/renameat.c
> +++ b/stdio-common/renameat.c
> @@ -22,7 +22,7 @@
>  
>  /* Rename the file OLD relative to OLDFD to NEW relative to NEWFD.  */
>  int
> -renameat (int oldfd, const char *old, int newfd, const char *new)
> +__renameat (int oldfd, const char *old, int newfd, const char *new)

OK.

>  {
>    if ((oldfd < 0 && oldfd != AT_FDCWD) || (newfd < 0 && newfd != AT_FDCWD))
>      {
> @@ -40,5 +40,6 @@ renameat (int oldfd, const char *old, int newfd, const char *new)
>    return -1;
>  }
>  
> -
> +libc_hidden_def (__renameat)
> +weak_alias (__renameat, renameat)

OK.

>  stub_warning (renameat)
> diff --git a/stdio-common/renameat2.c b/stdio-common/renameat2.c
> new file mode 100644
> index 0000000000..c2cedcd2cb
> --- /dev/null
> +++ b/stdio-common/renameat2.c
> @@ -0,0 +1,30 @@
> +/* Generic implementation of the renameat function.
> +   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 <stdio.h>
> +
> +int
> +renameat2 (int oldfd, const char *old, int newfd, const char *new,
> +           unsigned int flags)
> +{
> +  if (flags == 0)
> +    return __renameat (oldfd, old, newfd, new);
> +  __set_errno (EINVAL);
> +  return -1;

OK.

> +}
> diff --git a/stdio-common/tst-renameat2.c b/stdio-common/tst-renameat2.c
> new file mode 100644
> index 0000000000..958b0918d6
> --- /dev/null
> +++ b/stdio-common/tst-renameat2.c
> @@ -0,0 +1,204 @@
> +/* Linux implementation for renameat2 function.
> +   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 <array_length.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <support/check.h>
> +#include <support/support.h>
> +#include <support/temp_file.h>
> +#include <support/xunistd.h>
> +#include <unistd.h>
> +
> +/* Directory with the temporary files.  */
> +static char *directory;
> +static int directory_fd;
> +
> +/* Paths within that directory.  */
> +static char *old_path;          /* File is called "old".  */
> +static char *new_path;          /* File is called "new".  */
> +
> +/* Subdirectory within the directory above.  */
> +static char *subdirectory;
> +int subdirectory_fd;
> +
> +/* And a pathname in that directory (called "file").  */
> +static char *subdir_path;
> +
> +static void
> +prepare (int argc, char **argv)
> +{
> +  directory = support_create_temp_directory ("tst-renameat2-");
> +  directory_fd = xopen (directory, O_RDONLY | O_DIRECTORY, 0);
> +  old_path = xasprintf ("%s/old", directory);
> +  add_temp_file (old_path);
> +  new_path = xasprintf ("%s/new", directory);
> +  add_temp_file (new_path);
> +  subdirectory = xasprintf ("%s/subdir", directory);
> +  xmkdir (subdirectory, 0777);
> +  add_temp_file (subdirectory);
> +  subdirectory_fd = xopen (subdirectory, O_RDONLY | O_DIRECTORY, 0);
> +  subdir_path = xasprintf ("%s/file", subdirectory);
> +  add_temp_file (subdir_path);
> +}
> +
> +/* Delete all files, preparing a clean slate for the next test.  */
> +static void
> +delete_all_files (void)
> +{
> +  char *files[] = { old_path, new_path, subdir_path };
> +  for (size_t i = 0; i < array_length (files); ++i)
> +    if (unlink (files[i]) != 0 && errno != ENOENT)
> +      FAIL_EXIT1 ("unlink (\"%s\"): %m", files[i]);
> +}
> +
> +/* Return true if PATH exists in the file system.  */
> +static bool
> +file_exists (const char *path)
> +{
> +  return access (path, F_OK) == 0;
> +}
> +
> +/* Check that PATH exists and has size EXPECTED_SIZE.  */
> +static void
> +check_size (const char *path, off64_t expected_size)
> +{
> +  struct stat64 st;
> +  xstat (path, &st);
> +  if (st.st_size != expected_size)
> +    FAIL_EXIT1 ("file \"%s\": expected size %lld, actual size %lld",
> +                path, (unsigned long long int) expected_size,
> +                (unsigned long long int) st.st_size);
> +}
> +
> +/* Rename tests where the target does not exist.  */
> +static void
> +rename_without_existing_target (unsigned int flags)
> +{
> +  delete_all_files ();
> +  support_write_file_string (old_path, "");
> +  TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, flags), 0);
> +  TEST_VERIFY (!file_exists (old_path));
> +  TEST_VERIFY (file_exists (new_path));
> +
> +  delete_all_files ();
> +  support_write_file_string (old_path, "");
> +  TEST_COMPARE (renameat2 (directory_fd, "old", AT_FDCWD, new_path, flags), 0);
> +  TEST_VERIFY (!file_exists (old_path));
> +  TEST_VERIFY (file_exists (new_path));
> +
> +  delete_all_files ();
> +  support_write_file_string (old_path, "");
> +  TEST_COMPARE (renameat2 (directory_fd, "old", subdirectory_fd, "file", 0),
> +                0);
> +  TEST_VERIFY (!file_exists (old_path));
> +  TEST_VERIFY (file_exists (subdir_path));
> +}
> +
> +static int
> +do_test (void)
> +{
> +  /* Tests with zero flags argument.  These are expected to succeed
> +     because this renameat2 variant can be implemented with
> +     renameat.  */
> +  rename_without_existing_target (0);

OK.

> +
> +  /* renameat2 without flags replaces an existing destination.  */
> +  delete_all_files ();
> +  support_write_file_string (old_path, "123");
> +  support_write_file_string (new_path, "1234");
> +  TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, 0), 0);
> +  TEST_VERIFY (!file_exists (old_path));
> +  check_size (new_path, 3);
> +
> +  /* Now we need to check for kernel support of renameat2 with
> +     flags.  */
> +  delete_all_files ();
> +  support_write_file_string (old_path, "");
> +  if (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, RENAME_NOREPLACE)
> +      != 0)
> +    {
> +      if (errno == EINVAL)
> +        puts ("warning: no support for renameat2 with flags");
> +      else
> +        FAIL_EXIT1 ("renameat2 probe failed: %m");
> +    }
> +  else
> +    {
> +      /* We have full renameat2 support.  */
> +      rename_without_existing_target (RENAME_NOREPLACE);
> +
> +      /* Now test RENAME_NOREPLACE with an existing target.  */
> +      delete_all_files ();
> +      support_write_file_string (old_path, "123");
> +      support_write_file_string (new_path, "1234");
> +      TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path,
> +                               RENAME_NOREPLACE), -1);
> +      TEST_COMPARE (errno, EEXIST);
> +      check_size (old_path, 3);
> +      check_size (new_path, 4);
> +
> +      delete_all_files ();
> +      support_write_file_string (old_path, "123");
> +      support_write_file_string (new_path, "1234");
> +      TEST_COMPARE (renameat2 (directory_fd, "old", AT_FDCWD, new_path,
> +                               RENAME_NOREPLACE), -1);
> +      TEST_COMPARE (errno, EEXIST);
> +      check_size (old_path, 3);
> +      check_size (new_path, 4);
> +
> +      delete_all_files ();
> +      support_write_file_string (old_path, "123");
> +      support_write_file_string (subdir_path, "1234");
> +      TEST_COMPARE (renameat2 (directory_fd, "old", subdirectory_fd, "file",
> +                               RENAME_NOREPLACE), -1);
> +      TEST_COMPARE (errno, EEXIST);
> +      check_size (old_path, 3);
> +      check_size (subdir_path, 4);
> +
> +      /* The flag combination of RENAME_NOREPLACE and RENAME_EXCHANGE
> +         is invalid.  */
> +      TEST_COMPARE (renameat2 (directory_fd, "ignored",
> +                               subdirectory_fd, "ignored",
> +                               RENAME_NOREPLACE | RENAME_EXCHANGE), -1);
> +      TEST_COMPARE (errno, EINVAL);

OK.

> +    }
> +
> +  /* Create all the pathnames to avoid warnings from the test
> +     harness.  */
> +  support_write_file_string (old_path, "");
> +  support_write_file_string (new_path, "");
> +  support_write_file_string (subdir_path, "");

OK.

> +
> +  free (directory);
> +  free (subdirectory);
> +  free (old_path);
> +  free (new_path);
> +  free (subdir_path);
> +
> +  xclose (directory_fd);
> +  xclose (subdirectory_fd);
> +
> +  return 0;
> +}
> +
> +#define PREPARE prepare
> +#include <support/test-driver.c>
> diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
> index 3d46de795d..9df86e491f 100644
> --- a/sysdeps/mach/hurd/i386/libc.abilist
> +++ b/sysdeps/mach/hurd/i386/libc.abilist
> @@ -2035,6 +2035,7 @@ GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/mach/hurd/renameat.c b/sysdeps/mach/hurd/renameat.c
> index 43609600d9..7985763f73 100644
> --- a/sysdeps/mach/hurd/renameat.c
> +++ b/sysdeps/mach/hurd/renameat.c
> @@ -22,7 +22,7 @@
>  
>  /* Rename the file OLD relative to OLDFD to NEW relative to NEWFD.  */
>  int
> -renameat (int oldfd, const char *old, int newfd, const char *new)
> +__renameat (int oldfd, const char *old, int newfd, const char *new)

OK.

>  {
>    error_t err;
>    file_t olddir, newdir;
> @@ -45,3 +45,5 @@ renameat (int oldfd, const char *old, int newfd, const char *new)
>      return __hurd_fail (err);
>    return 0;
>  }
> +libc_hidden_def (__renameat)
> +weak_alias (__renameat, renameat)

OK.

> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> index 884d0dfa95..7a272a19bb 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> @@ -2132,3 +2132,4 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> index 28d54b9794..23fec55d97 100644
> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> @@ -2027,6 +2027,7 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
> index dfde3bd725..b203160889 100644
> --- a/sysdeps/unix/sysv/linux/arm/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
> @@ -117,6 +117,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.4 _Exit F
>  GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
>  GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
> diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> index 06b00f730a..64809ca403 100644
> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> @@ -1874,6 +1874,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
> index 1c1cc00d40..4a87f62ad9 100644
> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
> @@ -2039,6 +2039,7 @@ GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> index f6e17a005f..6bdd0d0443 100644
> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> @@ -1908,6 +1908,7 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
> index b90ea30195..ab42c5845a 100644
> --- a/sysdeps/unix/sysv/linux/kernel-features.h
> +++ b/sysdeps/unix/sysv/linux/kernel-features.h
> @@ -144,3 +144,8 @@
>     */
>  
>  #define __ASSUME_CLONE_DEFAULT 1
> +
> +/* Support for the renameat2 system call was added in kernel 3.15.  */
> +#if __LINUX_KERNEL_VERSION >= 0x030F00
> +# define __ASSUME_RENAMEAT2
> +#endif

Joseph commented on this regarding versions for each machine.

I also have no good way to compute this other than to review kernel code.

> diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> index ee054a618d..3226f916eb 100644
> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> @@ -118,6 +118,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.4 _Exit F
>  GLIBC_2.4 _IO_2_1_stderr_ D 0x98
>  GLIBC_2.4 _IO_2_1_stdin_ D 0x98
> diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> index 227a0581cb..b1074eeed1 100644
> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> @@ -1983,6 +1983,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> index 18781b3017..b52fc7d6f7 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> @@ -2124,3 +2124,4 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> index 2d86989cf2..718c74262a 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> @@ -1961,6 +1961,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> index b8b113e1a5..9b218a9435 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> @@ -1959,6 +1959,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> index 6a3cd13e2d..5a90ab83e7 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> @@ -1967,6 +1967,7 @@ GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> index 596ec05379..3005fc9500 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> @@ -1962,6 +1962,7 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> index 8da18eed57..a87fbbeb6b 100644
> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> @@ -2165,3 +2165,4 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> index 555751eb12..d56f776a52 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> @@ -1987,6 +1987,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> index 80324e41aa..2b5337aab6 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> @@ -1991,6 +1991,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> index 97b1d354af..d0dfde3897 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> @@ -2222,3 +2222,4 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> index 15be314921..d505ae0e7d 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> @@ -117,6 +117,7 @@ GLIBC_2.27 wcstof32x_l F
>  GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 _Exit F
>  GLIBC_2.3 _IO_2_1_stderr_ D 0xe0
>  GLIBC_2.3 _IO_2_1_stdin_ D 0xe0
> diff --git a/sysdeps/unix/sysv/linux/renameat.c b/sysdeps/unix/sysv/linux/renameat.c
> index 034432b934..f85c5ae0ec 100644
> --- a/sysdeps/unix/sysv/linux/renameat.c
> +++ b/sysdeps/unix/sysv/linux/renameat.c
> @@ -22,7 +22,7 @@
>  #include <errno.h>
>  
>  int
> -renameat (int oldfd, const char *old, int newfd, const char *new)
> +__renameat (int oldfd, const char *old, int newfd, const char *new)

OK.

>  {
>  #ifdef __NR_renameat
>    return INLINE_SYSCALL_CALL (renameat, oldfd, old, newfd, new);
> @@ -30,3 +30,5 @@ renameat (int oldfd, const char *old, int newfd, const char *new)
>    return INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, 0);
>  #endif
>  }
> +libc_hidden_def (__renameat)
> +weak_alias (__renameat, renameat)

OK.

> diff --git a/sysdeps/unix/sysv/linux/renameat2.c b/sysdeps/unix/sysv/linux/renameat2.c
> new file mode 100644
> index 0000000000..919bb2a0d4
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/renameat2.c
> @@ -0,0 +1,44 @@
> +/* Linux implementation for renameat2 function.

OK.

> +   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 <stdio.h>
> +#include <sysdep.h>
> +
> +int
> +renameat2 (int oldfd, const char *old, int newfd, const char *new,
> +           unsigned int flags)
> +{
> +#if !defined (__NR_renameat) || defined (__ASSUME_RENAMEAT2)
> +  return INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, flags);

OK.

> +#else
> +  if (flags == 0)
> +    return __renameat (oldfd, old, newfd, new);

OK. No specialized flags, use renameat with no possible race.

> +# ifdef __NR_renameat2
> +  /* For non-zero flags, try the renameat2 system call.  */
> +  int ret = INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, flags);
> +  if (ret != -1 || errno != ENOSYS)
> +    /* Preserve non-error/non-ENOSYS return values.  */
> +    return ret;

OK.

> +# endif
> +  /* No kernel (header) support for renameat2.  All flags are
> +     unknown.  */
> +  __set_errno (EINVAL);

OK. No racy fallback. EINVAL indicates we could call back with different
parameters e.g. no flags.

> +  return -1;
> +#endif
> +}
> diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> index 436b992251..33f751abc0 100644
> --- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> @@ -2094,3 +2094,4 @@ GLIBC_2.27 xencrypt F
>  GLIBC_2.27 xprt_register F
>  GLIBC_2.27 xprt_unregister F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> index f66715f0c9..c4ec93ff43 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> @@ -1996,6 +1996,7 @@ GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> index bd6242861b..2a6a0abde8 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> @@ -1901,6 +1901,7 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
> index f2f070fbce..8de0d1711a 100644
> --- a/sysdeps/unix/sysv/linux/sh/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
> @@ -1878,6 +1878,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> index 265087f6a8..9858460c9f 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> @@ -1990,6 +1990,7 @@ GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> index 16a69812cb..a22b8fb7ca 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> @@ -1931,6 +1931,7 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> index fa8c198d13..d5d71cccba 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> @@ -1889,6 +1889,7 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> index 2536971682..e6ad42440e 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> @@ -2140,3 +2140,4 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
> 

OK.
Paul Eggert July 4, 2018, 7:35 p.m. UTC | #19
Carlos O'Donell wrote:

> If we really need another non-race-free API then gnulib can provide that.

We do really need it. All existing uses of renameat2-like functionality in GNU 
applications already use the non-race-free API. But as I see it, the consensus 
is to banish this functionality to Gnulib. Since it will longer matter to Gnulib 
whether Glibc introduces renameat2, I withdraw my objection to the patch.

To help forestall likely bugs in code using Glibc renameat2, it would be helpful 
for the Glibc manual to document this issue, at least for RENAME_NOREPLACE which 
is the only reason I know anybody is calling renameat2 anyway. That is, if we're 
stuck with this API, at least we should warn users about its shortcomings. 
Perhaps the Glibc manual could point to Gnulib, as a suggestion for users who 
prefer the non-race-free functionality.

> We are not "fighting" against GNU applications
As an application writer it sure looks like a fight to me. Glibc is supplying an 
API that doesn't do what GNU apps need. So I plan to change the name of the 
Gnulib function to avoid namespace collisions, and as a result GnU apps via 
Gnulib will not use Glibc renameat2 at all. (Why should they bother? The Gnulib 
function already renames atomically using code that works on more platforms than 
Glibc does, and the Gnulib code is a tiny bit more efficient even when calling 
Glibc renameat2 would work.)

If this isn't "fighting" against GNU applications, I don't want to be around 
when a real fight occurs.

I understand the desire to support an API that guarantees atomicity, and I'm not 
arguing against that. All I'm saying is that Glibc should also support use cases 
that merely prefer instead of requiring atomicity, as these are so common in 
practice that we still haven't run into a GNU app that actually *requires* 
atomicity.
Paul Eggert July 4, 2018, 7:53 p.m. UTC | #20
Carlos O'Donell wrote:

> Suggest something like this:
> 
> * The library now implements the renameat2 function. On Linux systems that
>    support the renameat2 system call this is a wrapper for the kernel support
>    and avoids the race conditions present with renameat. No emulation is provide
>    and if renameat2 is not supported by the underlying operating system either
>    an errno of ENOSYS or EINVAL error will be returned.

This wording doesn't look right, since there is emulation in some cases even 
when renameat2 is not supported by the underlying operating system. That is, 
when FLAGS == 0, renameat2 is often emulated via renameat.  How about something 
like this wording instead?

* The library now implements the renameat2 function.  On Linux systems that
   support the renameat2 system call this is a wrapper for kernel support
   and when renameat2's flags are nonzero this should avoid race conditions
   present with renameat.  The renameat2 function fails with an errno of
   ENOSYS or EINVAL when given nonzero flags that cannot be implemented
   atomically by the kernel.
Carlos O'Donell July 4, 2018, 8:13 p.m. UTC | #21
On 07/04/2018 03:35 PM, Paul Eggert wrote:
> Carlos O'Donell wrote:
> 
>> If we really need another non-race-free API then gnulib can provide
>> that.
> 
> We do really need it. All existing uses of renameat2-like
> functionality in GNU applications already use the non-race-free API.
> But as I see it, the consensus is to banish this functionality to
> Gnulib. Since it will longer matter to Gnulib whether Glibc
> introduces renameat2, I withdraw my objection to the patch.
> 
> To help forestall likely bugs in code using Glibc renameat2, it would
> be helpful for the Glibc manual to document this issue, at least for
> RENAME_NOREPLACE which is the only reason I know anybody is calling
> renameat2 anyway. That is, if we're stuck with this API, at least we
> should warn users about its shortcomings. Perhaps the Glibc manual
> could point to Gnulib, as a suggestion for users who prefer the
> non-race-free functionality.

This is a good suggestion, and I think Florian should work on something
going into the manual to document the behaviour.

>> We are not "fighting" against GNU applications
> As an application writer it sure looks like a fight to me. Glibc is
> supplying an API that doesn't do what GNU apps need. So I plan to
> change the name of the Gnulib function to avoid namespace collisions,
> and as a result GnU apps via Gnulib will not use Glibc renameat2 at
> all. (Why should they bother? The Gnulib function already renames
> atomically using code that works on more platforms than Glibc does,
> and the Gnulib code is a tiny bit more efficient even when calling
> Glibc renameat2 would work.)

You position Gnulib's implementation as having no drawbacks, but this
is not true. The API has a race, and it is something which along with
other similar racy APIs has caused difficult to solve problems a the
distribution level.

I think it's a bad design choice to wrap renameat2 in a wrapper
that falls back to a racy solution. It can only cause us problems
because of the naming, in that renameat2 is not supposed to be racy.
However, I have no objection to a racy API with another name.

> I understand the desire to support an API that guarantees atomicity,
> and I'm not arguing against that. All I'm saying is that Glibc should
> also support use cases that merely prefer instead of requiring
> atomicity, as these are so common in practice that we still haven't
> run into a GNU app that actually *requires* atomicity.

coreutils *requires* it to solve the race, it says so in their NEWS:
~~~
  'mv -n A B' no longer suffers from a race condition that can
  overwrite a simultaneously-created B.  This bug fix requires
                                                      ^^^^^^^^
  platform support for the renameat2 or renameatx_np syscalls, found
  in recent Linux and macOS kernels.  As a side effect, ‘mv -n A A’
  now silently does nothing if A exists.
~~~
without it the fix regresses.

I don't object to a *distinct* API which implements the non-atomic variants.

I'm happy to review such new API additions, but that's not what is being
proposed in this thread by Florian.

In coreutils-8.30 lib/renameat2.c calls out at least 2 races which affect
correct operation. I would like *coretuils* itself to make the decision
in their sources, with full disclosure, to call the non-racy API first,
see if it works, and then if it doesn't work, attempt a racy API fallback.
This clearly documents the intended behaviour and is still easy for
application developers to use.

I don't think it's a good design choice to put both of those things together
into one API call. The semantics are sufficiently different that mistakes
will get made.

I want glibc to make a clear design decision here that these two things
should not be mixed in a single API, there is no strong reason to do so,
and in the past it has caused hard to find bugs.
Florian Weimer July 4, 2018, 8:26 p.m. UTC | #22
On 07/04/2018 10:13 PM, Carlos O'Donell wrote:
> This is a good suggestion, and I think Florian should work on something
> going into the manual to document the behaviour.

We do not have any documentation for the *at functions at present.  I 
find it difficult to document renameat2 without reference to openat and 
a generic description of the AT_* flags.  I feel this is something we 
should tackle after the release.

Once the patch is in, I will propose something for the existing manual 
page, documenting the EINVAL behavior of the glibc wrapper and the 
existence of the gnulib implementation.

> You position Gnulib's implementation as having no drawbacks, but this
> is not true. The API has a race, and it is something which along with
> other similar racy APIs has caused difficult to solve problems a the
> distribution level.

And as Joseph pointed out, there is a different emulation strategy with 
a different failure mode (use link and potentially leave behind a 
hard-linked file under both names).

Thanks,
Florian
Florian Weimer July 4, 2018, 8:28 p.m. UTC | #23
On 07/04/2018 09:53 PM, Paul Eggert wrote:
> Carlos O'Donell wrote:
> 
>> Suggest something like this:
>>
>> * The library now implements the renameat2 function. On Linux systems 
>> that
>>    support the renameat2 system call this is a wrapper for the kernel 
>> support
>>    and avoids the race conditions present with renameat. No emulation 
>> is provide
>>    and if renameat2 is not supported by the underlying operating 
>> system either
>>    an errno of ENOSYS or EINVAL error will be returned.
> 
> This wording doesn't look right, since there is emulation in some cases 
> even when renameat2 is not supported by the underlying operating system. 
> That is, when FLAGS == 0, renameat2 is often emulated via renameat.  How 
> about something like this wording instead?
> 
> * The library now implements the renameat2 function.  On Linux systems that
>    support the renameat2 system call this is a wrapper for kernel support
>    and when renameat2's flags are nonzero this should avoid race conditions
>    present with renameat.  The renameat2 function fails with an errno of
>    ENOSYS or EINVAL when given nonzero flags that cannot be implemented
>    atomically by the kernel.

I don't think the current renameat2 fallback code in glibc can return 
ENOSYS.  The generic renameat, which returns ENOSYS, is overriden by 
Linux and Hurd.

Thanks,
Florian
Paul Eggert July 4, 2018, 8:36 p.m. UTC | #24
Florian Weimer wrote:
>> * The library now implements the renameat2 function.  On Linux systems that
>>    support the renameat2 system call this is a wrapper for kernel support
>>    and when renameat2's flags are nonzero this should avoid race conditions
>>    present with renameat.  The renameat2 function fails with an errno of
>>    ENOSYS or EINVAL when given nonzero flags that cannot be implemented
>>    atomically by the kernel.
> 
> I don't think the current renameat2 fallback code in glibc can return ENOSYS.  
> The generic renameat, which returns ENOSYS, is overriden by Linux and Hurd.

Ah, OK, in that case please remove "ENOSYS or" from the proposed text.
Paul Eggert July 4, 2018, 8:46 p.m. UTC | #25
Florian Weimer wrote:
> as Joseph pointed out, there is a different emulation strategy with a different 
> failure mode (use link and potentially leave behind a hard-linked file under 
> both names).

So far I haven't run into applications that prefer that strategy, i.e., nobody 
seems using that strategy on older kernels that don't support renameat2 with 
RENAME_NOREPLACE. If we run into enough apps that prefer this fallback, we could 
add a flag for it of course.
Paul Eggert July 4, 2018, 8:47 p.m. UTC | #26
Carlos O'Donell wrote:
> On 07/04/2018 03:35 PM, Paul Eggert wrote:

> You position Gnulib's implementation as having no drawbacks

That certainly was not my intent. The Gnulib implementation has known race 
conditions on platforms whose kernels don't promise atomicity. It's just that 
Gnulib-using applications prefer this drawback to implementing racy subsititutes 
themselves.

Not every racy API is a bug. (If it were, we'd have to abolish stdio. :-)

> I have no objection to a racy API with another name.

Yes, that is direction we're heading.

>> we still haven't
>> run into a GNU app that actually *requires* atomicity.
> 
> coreutils *requires* it to solve the race

Sure, but Coreutils doesn't *require* atomicity to build and run just as well 
has it has run for decades. All Coreutils *requires* is the Gnulib API, which 
says "we'll try to avoid the race if we can". So far, GNU applications needing 
renameat2-like functionality have all fallen into the Coreutils camp.

> I would like *coretuils* itself to make the decision
> in their sources, with full disclosure,

Coreutils already does that. Coreutils uses Gnulib, which is a source-code 
library. Coreutils tarballs contain a copy of the source code that embody this 
decision and fully disclose it. And if Glibc added an API that supported 
Coreutils-like functionality here, programs using this new API would also be 
making the decision with full disclosure. So we should be OK here.
Carlos O'Donell July 5, 2018, 1:19 p.m. UTC | #27
On 07/04/2018 04:26 PM, Florian Weimer wrote:
> On 07/04/2018 10:13 PM, Carlos O'Donell wrote:
>> This is a good suggestion, and I think Florian should work on
>> something going into the manual to document the behaviour.
> 
> We do not have any documentation for the *at functions at present.  I
> find it difficult to document renameat2 without reference to openat
> and a generic description of the AT_* flags.  I feel this is
> something we should tackle after the release.

I'm OK with that.
 
> Once the patch is in, I will propose something for the existing
> manual page, documenting the EINVAL behavior of the glibc wrapper and
> the existence of the gnulib implementation.

That's a good short term solution to documenting the behaviour.
Florian Weimer July 5, 2018, 2:01 p.m. UTC | #28
On 07/04/2018 09:21 PM, Carlos O'Donell wrote:
> On 06/30/2018 08:14 AM, Florian Weimer wrote:
>> The implementation falls back to renameat if renameat2 is not available
>> in the kernel (or in the kernel headers) and the flags argument is zero.
>> Without kernel support, a non-zero argument returns EINVAL, not ENOSYS.
>> This mirrors what the kernel does for invalid renameat2 flags.
> 
> This looks good to me. I object to adding emulation to this function.

Here's an updated patch with the typo fix, the kernel version updates, 
and the expanded NEWS entry.

Still okay?

Thanks,
Florian
Subject: [PATCH] Add renameat2 function [BZ #17662]
To: libc-alpha@sourceware.org

The implementation falls back to renameat if renameat2 is not available
in the kernel (or in the kernel headers) and the flags argument is zero.
Without kernel support, a non-zero argument returns EINVAL, not ENOSYS.
This mirrors what the kernel does for invalid renameat2 flags.

2018-07-05  Florian Weimer  <fweimer@redhat.com>

	[BZ # 17662]
	* libio/stdio.h [__USE_GNU] (RENAME_NOREPLACE, RENAME_EXCHANGE)
	(RENAME_WHITEOUT): Define.
	[__USE_GNU] (renameat2): Declare.
	* stdio-common/Makefile (routines): Add renameat2.
	(tests): Add tst-renameat2.
	* stdio-common/Versions (GLIBC_2_28): Export renameat2.
	* stdio-common/renameat2.c: New file.
	* stdio-common/tst-renameat2.c: Likewise.
	* sysdeps/unix/sysv/linux/renameat2.c: Likewise.
	* manual/filesys.texi (Temporary Files): Note that renameat2 is
	undocumented.
	* sysdeps/unix/sysv/linux/kernel-features.h
	[__LINUX_KERNEL_VERSION >= 0x030F00] (__ASSUME_RENAMEAT2): Define.
	* sysdeps/unix/sysv/linux/alpha/kernel-features.h
	[__LINUX_KERNEL_VERSION < 0x031100] (__ASSUME_RENAMEAT2): Undefine.
	* sysdeps/unix/sysv/linux/microblaze/kernel-features.h
	[__LINUX_KERNEL_VERSION < 0x031100] (__ASSUME_RENAMEAT2): Undefine.
	* sysdeps/unix/sysv/linux/sh/kernel-features.h
	[__LINUX_KERNEL_VERSION < 0x040800] (__ASSUME_RENAMEAT2): Undefine.
	* sysdeps/unix/sysv/linux/sparc/kernel-features.h
	[__LINUX_KERNEL_VERSION < 0x031000] (__ASSUME_RENAMEAT2): Undefine.
	* include/stdio.h (__renameat): Add alias for renameat.
	* stdio-common/renameat.c (__renameat): Rename from renameat.
	Add hidden definition and alias.
	* sysdeps/unix/sysv/linux/renameat.c: Likewise.
	* sysdeps/mach/hurd/renameat.c: Likewise.
	* sysdeps/**/libc*.abilist: Add renameat2.

diff --git a/NEWS b/NEWS
index b1ce067d27..9b0ce56778 100644
--- a/NEWS
+++ b/NEWS
@@ -39,6 +39,15 @@ Major new features:
 * Building and running on GNU/Hurd systems now works without out-of-tree
   patches.
 
+* The renameat2 function has been added, a variant of the renameat function
+  which has a flags argument.  If the flags are zero, the renameat2 function
+  is implemented using renameat.  If the flag is not zero and there is no
+  kernel support for renameat2, the function will fail with an errno value
+  of EINVAL.  This is different from the existing gnulib function renameat2,
+  which performs a plain rename operation in case of a RENAME_NOREPLACE
+  flags and a non-existing destination (and therefore has a race condition
+  that can clobber the destination inadvertently).
+
 * IDN domain names in getaddrinfo and getnameinfo now use the system libidn2
   library if installed.  libidn2 version 2.0.5 or later is recommended.  If
   libidn2 is not available, internationalized domain names are not encoded
diff --git a/include/stdio.h b/include/stdio.h
index f140813ad6..3ba0edc924 100644
--- a/include/stdio.h
+++ b/include/stdio.h
@@ -237,5 +237,8 @@ __putc_unlocked (int __c, FILE *__stream)
 }
 #  endif
 
+extern __typeof (renameat) __renameat;
+libc_hidden_proto (__renameat)
+
 # endif /* not _ISOMAC */
 #endif /* stdio.h */
diff --git a/libio/stdio.h b/libio/stdio.h
index 731f8e56f4..739e08610d 100644
--- a/libio/stdio.h
+++ b/libio/stdio.h
@@ -153,6 +153,18 @@ extern int renameat (int __oldfd, const char *__old, int __newfd,
 		     const char *__new) __THROW;
 #endif
 
+#ifdef __USE_GNU
+/* Flags for renameat2.  */
+# define RENAME_NOREPLACE (1 << 0)
+# define RENAME_EXCHANGE (1 << 1)
+# define RENAME_WHITEOUT (1 << 2)
+
+/* Rename file OLD relative to OLDFD to NEW relative to NEWFD, with
+   additional flags.  */
+extern int renameat2 (int __oldfd, const char *__old, int __newfd,
+		      const char *__new, unsigned int __flags) __THROW;
+#endif
+
 /* Create a temporary file and open it read/write.
 
    This function is a possible cancellation point and therefore not
diff --git a/manual/filesys.texi b/manual/filesys.texi
index cc70a6b7ee..db2f1041de 100644
--- a/manual/filesys.texi
+++ b/manual/filesys.texi
@@ -3552,6 +3552,7 @@ The @code{mkdtemp} function comes from OpenBSD.
 @c open_by_handle_at
 @c readlinkat
 @c renameat
+@c renameat2
 @c scandirat
 @c symlinkat
 @c unlinkat
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 96bd7c303a..a10f12ab3c 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -35,7 +35,7 @@ routines	:=							      \
 	perror psignal							      \
 	tmpfile tmpfile64 tmpnam tmpnam_r tempnam tempname		      \
 	getline getw putw						      \
-	remove rename renameat						      \
+	remove rename renameat renameat2				      \
 	flockfile ftrylockfile funlockfile				      \
 	isoc99_scanf isoc99_vscanf isoc99_fscanf isoc99_vfscanf isoc99_sscanf \
 	isoc99_vsscanf							      \
@@ -62,6 +62,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
 	 tst-vfprintf-user-type \
 	 tst-vfprintf-mbs-prec \
 	 tst-scanf-round \
+	 tst-renameat2 \
 
 test-srcs = tst-unbputc tst-printf tst-printfsz-islongdouble
 
diff --git a/stdio-common/Versions b/stdio-common/Versions
index 5016f69c20..b8217578c8 100644
--- a/stdio-common/Versions
+++ b/stdio-common/Versions
@@ -57,6 +57,9 @@ libc {
     psiginfo;
     register_printf_modifier; register_printf_type; register_printf_specifier;
   }
+  GLIBC_2.28 {
+    renameat2;
+  }
   GLIBC_PRIVATE {
     # global variables
     _itoa_lower_digits;
diff --git a/stdio-common/renameat.c b/stdio-common/renameat.c
index 2180b87bdf..98c8f1d18b 100644
--- a/stdio-common/renameat.c
+++ b/stdio-common/renameat.c
@@ -22,7 +22,7 @@
 
 /* Rename the file OLD relative to OLDFD to NEW relative to NEWFD.  */
 int
-renameat (int oldfd, const char *old, int newfd, const char *new)
+__renameat (int oldfd, const char *old, int newfd, const char *new)
 {
   if ((oldfd < 0 && oldfd != AT_FDCWD) || (newfd < 0 && newfd != AT_FDCWD))
     {
@@ -40,5 +40,6 @@ renameat (int oldfd, const char *old, int newfd, const char *new)
   return -1;
 }
 
-
+libc_hidden_def (__renameat)
+weak_alias (__renameat, renameat)
 stub_warning (renameat)
diff --git a/stdio-common/renameat2.c b/stdio-common/renameat2.c
new file mode 100644
index 0000000000..c2cedcd2cb
--- /dev/null
+++ b/stdio-common/renameat2.c
@@ -0,0 +1,30 @@
+/* Generic implementation of the renameat function.
+   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 <stdio.h>
+
+int
+renameat2 (int oldfd, const char *old, int newfd, const char *new,
+           unsigned int flags)
+{
+  if (flags == 0)
+    return __renameat (oldfd, old, newfd, new);
+  __set_errno (EINVAL);
+  return -1;
+}
diff --git a/stdio-common/tst-renameat2.c b/stdio-common/tst-renameat2.c
new file mode 100644
index 0000000000..958b0918d6
--- /dev/null
+++ b/stdio-common/tst-renameat2.c
@@ -0,0 +1,204 @@
+/* Linux implementation for renameat2 function.
+   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 <array_length.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/xunistd.h>
+#include <unistd.h>
+
+/* Directory with the temporary files.  */
+static char *directory;
+static int directory_fd;
+
+/* Paths within that directory.  */
+static char *old_path;          /* File is called "old".  */
+static char *new_path;          /* File is called "new".  */
+
+/* Subdirectory within the directory above.  */
+static char *subdirectory;
+int subdirectory_fd;
+
+/* And a pathname in that directory (called "file").  */
+static char *subdir_path;
+
+static void
+prepare (int argc, char **argv)
+{
+  directory = support_create_temp_directory ("tst-renameat2-");
+  directory_fd = xopen (directory, O_RDONLY | O_DIRECTORY, 0);
+  old_path = xasprintf ("%s/old", directory);
+  add_temp_file (old_path);
+  new_path = xasprintf ("%s/new", directory);
+  add_temp_file (new_path);
+  subdirectory = xasprintf ("%s/subdir", directory);
+  xmkdir (subdirectory, 0777);
+  add_temp_file (subdirectory);
+  subdirectory_fd = xopen (subdirectory, O_RDONLY | O_DIRECTORY, 0);
+  subdir_path = xasprintf ("%s/file", subdirectory);
+  add_temp_file (subdir_path);
+}
+
+/* Delete all files, preparing a clean slate for the next test.  */
+static void
+delete_all_files (void)
+{
+  char *files[] = { old_path, new_path, subdir_path };
+  for (size_t i = 0; i < array_length (files); ++i)
+    if (unlink (files[i]) != 0 && errno != ENOENT)
+      FAIL_EXIT1 ("unlink (\"%s\"): %m", files[i]);
+}
+
+/* Return true if PATH exists in the file system.  */
+static bool
+file_exists (const char *path)
+{
+  return access (path, F_OK) == 0;
+}
+
+/* Check that PATH exists and has size EXPECTED_SIZE.  */
+static void
+check_size (const char *path, off64_t expected_size)
+{
+  struct stat64 st;
+  xstat (path, &st);
+  if (st.st_size != expected_size)
+    FAIL_EXIT1 ("file \"%s\": expected size %lld, actual size %lld",
+                path, (unsigned long long int) expected_size,
+                (unsigned long long int) st.st_size);
+}
+
+/* Rename tests where the target does not exist.  */
+static void
+rename_without_existing_target (unsigned int flags)
+{
+  delete_all_files ();
+  support_write_file_string (old_path, "");
+  TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, flags), 0);
+  TEST_VERIFY (!file_exists (old_path));
+  TEST_VERIFY (file_exists (new_path));
+
+  delete_all_files ();
+  support_write_file_string (old_path, "");
+  TEST_COMPARE (renameat2 (directory_fd, "old", AT_FDCWD, new_path, flags), 0);
+  TEST_VERIFY (!file_exists (old_path));
+  TEST_VERIFY (file_exists (new_path));
+
+  delete_all_files ();
+  support_write_file_string (old_path, "");
+  TEST_COMPARE (renameat2 (directory_fd, "old", subdirectory_fd, "file", 0),
+                0);
+  TEST_VERIFY (!file_exists (old_path));
+  TEST_VERIFY (file_exists (subdir_path));
+}
+
+static int
+do_test (void)
+{
+  /* Tests with zero flags argument.  These are expected to succeed
+     because this renameat2 variant can be implemented with
+     renameat.  */
+  rename_without_existing_target (0);
+
+  /* renameat2 without flags replaces an existing destination.  */
+  delete_all_files ();
+  support_write_file_string (old_path, "123");
+  support_write_file_string (new_path, "1234");
+  TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, 0), 0);
+  TEST_VERIFY (!file_exists (old_path));
+  check_size (new_path, 3);
+
+  /* Now we need to check for kernel support of renameat2 with
+     flags.  */
+  delete_all_files ();
+  support_write_file_string (old_path, "");
+  if (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, RENAME_NOREPLACE)
+      != 0)
+    {
+      if (errno == EINVAL)
+        puts ("warning: no support for renameat2 with flags");
+      else
+        FAIL_EXIT1 ("renameat2 probe failed: %m");
+    }
+  else
+    {
+      /* We have full renameat2 support.  */
+      rename_without_existing_target (RENAME_NOREPLACE);
+
+      /* Now test RENAME_NOREPLACE with an existing target.  */
+      delete_all_files ();
+      support_write_file_string (old_path, "123");
+      support_write_file_string (new_path, "1234");
+      TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path,
+                               RENAME_NOREPLACE), -1);
+      TEST_COMPARE (errno, EEXIST);
+      check_size (old_path, 3);
+      check_size (new_path, 4);
+
+      delete_all_files ();
+      support_write_file_string (old_path, "123");
+      support_write_file_string (new_path, "1234");
+      TEST_COMPARE (renameat2 (directory_fd, "old", AT_FDCWD, new_path,
+                               RENAME_NOREPLACE), -1);
+      TEST_COMPARE (errno, EEXIST);
+      check_size (old_path, 3);
+      check_size (new_path, 4);
+
+      delete_all_files ();
+      support_write_file_string (old_path, "123");
+      support_write_file_string (subdir_path, "1234");
+      TEST_COMPARE (renameat2 (directory_fd, "old", subdirectory_fd, "file",
+                               RENAME_NOREPLACE), -1);
+      TEST_COMPARE (errno, EEXIST);
+      check_size (old_path, 3);
+      check_size (subdir_path, 4);
+
+      /* The flag combination of RENAME_NOREPLACE and RENAME_EXCHANGE
+         is invalid.  */
+      TEST_COMPARE (renameat2 (directory_fd, "ignored",
+                               subdirectory_fd, "ignored",
+                               RENAME_NOREPLACE | RENAME_EXCHANGE), -1);
+      TEST_COMPARE (errno, EINVAL);
+    }
+
+  /* Create all the pathnames to avoid warnings from the test
+     harness.  */
+  support_write_file_string (old_path, "");
+  support_write_file_string (new_path, "");
+  support_write_file_string (subdir_path, "");
+
+  free (directory);
+  free (subdirectory);
+  free (old_path);
+  free (new_path);
+  free (subdir_path);
+
+  xclose (directory_fd);
+  xclose (subdirectory_fd);
+
+  return 0;
+}
+
+#define PREPARE prepare
+#include <support/test-driver.c>
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index 57091fce00..a9089d9064 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2034,6 +2034,7 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/mach/hurd/renameat.c b/sysdeps/mach/hurd/renameat.c
index 43609600d9..7985763f73 100644
--- a/sysdeps/mach/hurd/renameat.c
+++ b/sysdeps/mach/hurd/renameat.c
@@ -22,7 +22,7 @@
 
 /* Rename the file OLD relative to OLDFD to NEW relative to NEWFD.  */
 int
-renameat (int oldfd, const char *old, int newfd, const char *new)
+__renameat (int oldfd, const char *old, int newfd, const char *new)
 {
   error_t err;
   file_t olddir, newdir;
@@ -45,3 +45,5 @@ renameat (int oldfd, const char *old, int newfd, const char *new)
     return __hurd_fail (err);
   return 0;
 }
+libc_hidden_def (__renameat)
+weak_alias (__renameat, renameat)
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 884d0dfa95..7a272a19bb 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2132,3 +2132,4 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
diff --git a/sysdeps/unix/sysv/linux/alpha/kernel-features.h b/sysdeps/unix/sysv/linux/alpha/kernel-features.h
index c2d4a9f55b..5781cffd5a 100644
--- a/sysdeps/unix/sysv/linux/alpha/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/alpha/kernel-features.h
@@ -35,6 +35,11 @@
 #define __ASSUME_RECV_SYSCALL	1
 #define __ASSUME_SEND_SYSCALL	1
 
+/* Support for the renameat2 syscall was added in 3.17.  */
+#if __LINUX_KERNEL_VERSION < 0x031100
+# undef __ASSUME_RENAMEAT2
+#endif
+
 /* Support for the execveat syscall was added in 4.2.  */
 #if __LINUX_KERNEL_VERSION < 0x040200
 # undef __ASSUME_EXECVEAT
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 28d54b9794..23fec55d97 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2027,6 +2027,7 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index dfde3bd725..b203160889 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -117,6 +117,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 06b00f730a..64809ca403 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -1874,6 +1874,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 1c1cc00d40..4a87f62ad9 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2039,6 +2039,7 @@ GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index f6e17a005f..6bdd0d0443 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -1908,6 +1908,7 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
index b90ea30195..86fc66574b 100644
--- a/sysdeps/unix/sysv/linux/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/kernel-features.h
@@ -97,6 +97,11 @@
    implementation based on p{read,write}v and returning an error for
    non supported flags.  */
 
+/* Support for the renameat2 system call was added in kernel 3.15.  */
+#if __LINUX_KERNEL_VERSION >= 0x030F00
+# define __ASSUME_RENAMEAT2
+#endif
+
 /* Support for the execveat syscall was added in 3.19.  */
 #if __LINUX_KERNEL_VERSION >= 0x031300
 # define __ASSUME_EXECVEAT	1
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index ee054a618d..3226f916eb 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -118,6 +118,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0x98
 GLIBC_2.4 _IO_2_1_stdin_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 227a0581cb..b1074eeed1 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -1983,6 +1983,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/microblaze/kernel-features.h b/sysdeps/unix/sysv/linux/microblaze/kernel-features.h
index b13b863f06..7db3d05636 100644
--- a/sysdeps/unix/sysv/linux/microblaze/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/microblaze/kernel-features.h
@@ -48,6 +48,11 @@
 # undef __ASSUME_SENDMMSG_SYSCALL
 #endif
 
+/* Support for the renameat2 syscall was added in 3.17.  */
+#if __LINUX_KERNEL_VERSION < 0x031100
+# undef __ASSUME_RENAMEAT2
+#endif
+
 /* Support for the execveat syscall was added in 4.0.  */
 #if __LINUX_KERNEL_VERSION < 0x040000
 # undef __ASSUME_EXECVEAT
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index 18781b3017..b52fc7d6f7 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2124,3 +2124,4 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 2d86989cf2..718c74262a 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -1961,6 +1961,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index b8b113e1a5..9b218a9435 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -1959,6 +1959,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 6a3cd13e2d..5a90ab83e7 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -1967,6 +1967,7 @@ GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 596ec05379..3005fc9500 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -1962,6 +1962,7 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 8da18eed57..a87fbbeb6b 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2165,3 +2165,4 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 555751eb12..d56f776a52 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -1987,6 +1987,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index 80324e41aa..2b5337aab6 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -1991,6 +1991,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
index 97b1d354af..d0dfde3897 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
@@ -2222,3 +2222,4 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
index 15be314921..d505ae0e7d 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
@@ -117,6 +117,7 @@ GLIBC_2.27 wcstof32x_l F
 GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 _Exit F
 GLIBC_2.3 _IO_2_1_stderr_ D 0xe0
 GLIBC_2.3 _IO_2_1_stdin_ D 0xe0
diff --git a/sysdeps/unix/sysv/linux/renameat.c b/sysdeps/unix/sysv/linux/renameat.c
index 034432b934..f85c5ae0ec 100644
--- a/sysdeps/unix/sysv/linux/renameat.c
+++ b/sysdeps/unix/sysv/linux/renameat.c
@@ -22,7 +22,7 @@
 #include <errno.h>
 
 int
-renameat (int oldfd, const char *old, int newfd, const char *new)
+__renameat (int oldfd, const char *old, int newfd, const char *new)
 {
 #ifdef __NR_renameat
   return INLINE_SYSCALL_CALL (renameat, oldfd, old, newfd, new);
@@ -30,3 +30,5 @@ renameat (int oldfd, const char *old, int newfd, const char *new)
   return INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, 0);
 #endif
 }
+libc_hidden_def (__renameat)
+weak_alias (__renameat, renameat)
diff --git a/sysdeps/unix/sysv/linux/renameat2.c b/sysdeps/unix/sysv/linux/renameat2.c
new file mode 100644
index 0000000000..919bb2a0d4
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/renameat2.c
@@ -0,0 +1,44 @@
+/* Linux implementation for renameat2 function.
+   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 <stdio.h>
+#include <sysdep.h>
+
+int
+renameat2 (int oldfd, const char *old, int newfd, const char *new,
+           unsigned int flags)
+{
+#if !defined (__NR_renameat) || defined (__ASSUME_RENAMEAT2)
+  return INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, flags);
+#else
+  if (flags == 0)
+    return __renameat (oldfd, old, newfd, new);
+# ifdef __NR_renameat2
+  /* For non-zero flags, try the renameat2 system call.  */
+  int ret = INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, flags);
+  if (ret != -1 || errno != ENOSYS)
+    /* Preserve non-error/non-ENOSYS return values.  */
+    return ret;
+# endif
+  /* No kernel (header) support for renameat2.  All flags are
+     unknown.  */
+  __set_errno (EINVAL);
+  return -1;
+#endif
+}
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 436b992251..33f751abc0 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2094,3 +2094,4 @@ GLIBC_2.27 xencrypt F
 GLIBC_2.27 xprt_register F
 GLIBC_2.27 xprt_unregister F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index f66715f0c9..c4ec93ff43 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -1996,6 +1996,7 @@ GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index bd6242861b..2a6a0abde8 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -1901,6 +1901,7 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/sh/kernel-features.h b/sysdeps/unix/sysv/linux/sh/kernel-features.h
index b82d032e6b..05b7dcd037 100644
--- a/sysdeps/unix/sysv/linux/sh/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/sh/kernel-features.h
@@ -51,4 +51,9 @@
 /* sh only supports ipc syscall.  */
 #undef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
 
+/* Support for the renameat2 syscall was added in 4.8.  */
+#if __LINUX_KERNEL_VERSION < 0x040800
+# undef __ASSUME_RENAMEAT2
+#endif
+
 #endif
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index f2f070fbce..8de0d1711a 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -1878,6 +1878,7 @@ GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/sparc/kernel-features.h b/sysdeps/unix/sysv/linux/sparc/kernel-features.h
index 64d714036e..91990a716f 100644
--- a/sysdeps/unix/sysv/linux/sparc/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/sparc/kernel-features.h
@@ -41,6 +41,11 @@
 /* sparc only supports ipc syscall.  */
 #undef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
 
+/* Support for the renameat2 syscall was added in 3.16.  */
+#if __LINUX_KERNEL_VERSION < 0x031000
+# undef __ASSUME_RENAMEAT2
+#endif
+
 /* SPARC kernel Kconfig does not define CONFIG_CLONE_BACKWARDS, however it
    has the same ABI as if it did, implemented by sparc-specific code
    (sparc_do_fork).
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 265087f6a8..9858460c9f 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -1990,6 +1990,7 @@ GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 16a69812cb..a22b8fb7ca 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -1931,6 +1931,7 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index fa8c198d13..d5d71cccba 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1889,6 +1889,7 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 2536971682..e6ad42440e 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2140,3 +2140,4 @@ GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
Paul Eggert July 5, 2018, 2:24 p.m. UTC | #29
Thanks, everything looks good, except for one nit in the NEWS patch, which says:

> If the flags are zero, the renameat2 function is implemented using renameat.  If the flag is not zero and there is no kernel support for renameat2, the function will fail with an errno value of EINVAL.


If the flags are zero, renameat2 is not always implemented using renameat. Also,
I found that second sentence confusing (it doesn't say what happens if there is 
kernel support). How about the following text instead?

---

If the flags are zero, the renameat2 function is equivalent to renameat. 
Otherwise, it uses the renameat2 system call if kernel support is available, and 
fails with an errno value of EINVAL otherwise.
Florian Weimer July 5, 2018, 3:25 p.m. UTC | #30
On 07/05/2018 04:24 PM, Paul Eggert wrote:
> Thanks, everything looks good, except for one nit in the NEWS patch, 
> which says:
> 
>> If the flags are zero, the renameat2 function is implemented using 
>> renameat.  If the flag is not zero and there is no kernel support for 
>> renameat2, the function will fail with an errno value of EINVAL.
> 
> 
> If the flags are zero, renameat2 is not always implemented using 
> renameat. Also,
> I found that second sentence confusing (it doesn't say what happens if 
> there is kernel support). How about the following text instead?
> 
> ---
> 
> If the flags are zero, the renameat2 function is equivalent to renameat. 
> Otherwise, it uses the renameat2 system call if kernel support is 
> available, and fails with an errno value of EINVAL otherwise.

Hurd doesn't have system calls, so I'm not sure if this language is 
appropriate.  So I would like to use this:

* The renameat2 function has been added, a variant of the
   renameat function which has a flags argument.  If the flags are
   zero, the renameat2 function acts like renameat.  If the flag
   is not zero and there is no kernel support for renameat2, the
   function will fail with an errno value of EINVAL.  This is
   different from the existing gnulib function renameat2, which
   performs a plain rename operation in case of a RENAME_NOREPLACE
   flags and a non-existing destination (and therefore has a race
   condition that can clobber the destination inadvertently).

Thanks,
Florian
Paul Eggert July 5, 2018, 4:53 p.m. UTC | #31
On 07/05/2018 08:25 AM, Florian Weimer wrote:
> I would like to use this:

Looks good except one more thing, as I just now installed the patch to 
Gnulib that renames its renameat2 to renameatu: please change "gnulib 
function renameat2" to "gnulib function renameatu (formerly renameat2)" 
in the NEWS entry. Thanks.
Carlos O'Donell July 5, 2018, 4:57 p.m. UTC | #32
On 07/05/2018 10:01 AM, Florian Weimer wrote:
> On 07/04/2018 09:21 PM, Carlos O'Donell wrote:
>> On 06/30/2018 08:14 AM, Florian Weimer wrote:
>>> The implementation falls back to renameat if renameat2 is not available
>>> in the kernel (or in the kernel headers) and the flags argument is zero.
>>> Without kernel support, a non-zero argument returns EINVAL, not ENOSYS.
>>> This mirrors what the kernel does for invalid renameat2 flags.
>>
>> This looks good to me. I object to adding emulation to this function.
> 
> Here's an updated patch with the typo fix, the kernel version updates, and the expanded NEWS entry.
> 
> Still okay?

This is OK for glibc 2.28 release.

v2
- Resolves my request for NEWS entry enhancement.
- Resolves Joseph's comments about the differing kernel version introductions
  for renameat2 (alpha, microblaze, sh, and sparc have different versions now).
- Resolves Yury's comment about the typo in header.

With NEWS:
~~~
* The renameat2 function has been added, a variant of the
  renameat function which has a flags argument.  If the flags are
  zero, the renameat2 function acts like renameat.  If the flag
  is not zero and there is no kernel support for renameat2, the
  function will fail with an errno value of EINVAL.  This is
  different from the existing gnulib function renameatu, which
  performs a plain rename operation in case of a RENAME_NOREPLACE
  flags and a non-existing destination (and therefore has a race
  condition that can clobber the destination inadvertently). 
~~~
- New downthread NEWS from you.
- Includes Paul's request to use 'renameatu'.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>

> 
> Subject: [PATCH] Add renameat2 function [BZ #17662]
> To: libc-alpha@sourceware.org
> 
> The implementation falls back to renameat if renameat2 is not available
> in the kernel (or in the kernel headers) and the flags argument is zero.
> Without kernel support, a non-zero argument returns EINVAL, not ENOSYS.
> This mirrors what the kernel does for invalid renameat2 flags.
> 
> 2018-07-05  Florian Weimer  <fweimer@redhat.com>
> 
> 	[BZ # 17662]
> 	* libio/stdio.h [__USE_GNU] (RENAME_NOREPLACE, RENAME_EXCHANGE)
> 	(RENAME_WHITEOUT): Define.
> 	[__USE_GNU] (renameat2): Declare.
> 	* stdio-common/Makefile (routines): Add renameat2.
> 	(tests): Add tst-renameat2.
> 	* stdio-common/Versions (GLIBC_2_28): Export renameat2.
> 	* stdio-common/renameat2.c: New file.
> 	* stdio-common/tst-renameat2.c: Likewise.
> 	* sysdeps/unix/sysv/linux/renameat2.c: Likewise.
> 	* manual/filesys.texi (Temporary Files): Note that renameat2 is
> 	undocumented.
> 	* sysdeps/unix/sysv/linux/kernel-features.h
> 	[__LINUX_KERNEL_VERSION >= 0x030F00] (__ASSUME_RENAMEAT2): Define.
> 	* sysdeps/unix/sysv/linux/alpha/kernel-features.h
> 	[__LINUX_KERNEL_VERSION < 0x031100] (__ASSUME_RENAMEAT2): Undefine.
> 	* sysdeps/unix/sysv/linux/microblaze/kernel-features.h
> 	[__LINUX_KERNEL_VERSION < 0x031100] (__ASSUME_RENAMEAT2): Undefine.
> 	* sysdeps/unix/sysv/linux/sh/kernel-features.h
> 	[__LINUX_KERNEL_VERSION < 0x040800] (__ASSUME_RENAMEAT2): Undefine.
> 	* sysdeps/unix/sysv/linux/sparc/kernel-features.h
> 	[__LINUX_KERNEL_VERSION < 0x031000] (__ASSUME_RENAMEAT2): Undefine.
> 	* include/stdio.h (__renameat): Add alias for renameat.
> 	* stdio-common/renameat.c (__renameat): Rename from renameat.
> 	Add hidden definition and alias.
> 	* sysdeps/unix/sysv/linux/renameat.c: Likewise.
> 	* sysdeps/mach/hurd/renameat.c: Likewise.
> 	* sysdeps/**/libc*.abilist: Add renameat2.
> 
> diff --git a/NEWS b/NEWS
> index b1ce067d27..9b0ce56778 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -39,6 +39,15 @@ Major new features:
>  * Building and running on GNU/Hurd systems now works without out-of-tree
>    patches.
>  
> +* The renameat2 function has been added, a variant of the renameat function
> +  which has a flags argument.  If the flags are zero, the renameat2 function
> +  is implemented using renameat.  If the flag is not zero and there is no
> +  kernel support for renameat2, the function will fail with an errno value
> +  of EINVAL.  This is different from the existing gnulib function renameat2,
> +  which performs a plain rename operation in case of a RENAME_NOREPLACE
> +  flags and a non-existing destination (and therefore has a race condition
> +  that can clobber the destination inadvertently).
> +
>  * IDN domain names in getaddrinfo and getnameinfo now use the system libidn2
>    library if installed.  libidn2 version 2.0.5 or later is recommended.  If
>    libidn2 is not available, internationalized domain names are not encoded
> diff --git a/include/stdio.h b/include/stdio.h
> index f140813ad6..3ba0edc924 100644
> --- a/include/stdio.h
> +++ b/include/stdio.h
> @@ -237,5 +237,8 @@ __putc_unlocked (int __c, FILE *__stream)
>  }
>  #  endif
>  
> +extern __typeof (renameat) __renameat;
> +libc_hidden_proto (__renameat)
> +
>  # endif /* not _ISOMAC */
>  #endif /* stdio.h */
> diff --git a/libio/stdio.h b/libio/stdio.h
> index 731f8e56f4..739e08610d 100644
> --- a/libio/stdio.h
> +++ b/libio/stdio.h
> @@ -153,6 +153,18 @@ extern int renameat (int __oldfd, const char *__old, int __newfd,
>  		     const char *__new) __THROW;
>  #endif
>  
> +#ifdef __USE_GNU
> +/* Flags for renameat2.  */

OK.

> +# define RENAME_NOREPLACE (1 << 0)
> +# define RENAME_EXCHANGE (1 << 1)
> +# define RENAME_WHITEOUT (1 << 2)
> +
> +/* Rename file OLD relative to OLDFD to NEW relative to NEWFD, with
> +   additional flags.  */
> +extern int renameat2 (int __oldfd, const char *__old, int __newfd,
> +		      const char *__new, unsigned int __flags) __THROW;
> +#endif
> +
>  /* Create a temporary file and open it read/write.
>  
>     This function is a possible cancellation point and therefore not
> diff --git a/manual/filesys.texi b/manual/filesys.texi
> index cc70a6b7ee..db2f1041de 100644
> --- a/manual/filesys.texi
> +++ b/manual/filesys.texi
> @@ -3552,6 +3552,7 @@ The @code{mkdtemp} function comes from OpenBSD.
>  @c open_by_handle_at
>  @c readlinkat
>  @c renameat
> +@c renameat2
>  @c scandirat
>  @c symlinkat
>  @c unlinkat
> diff --git a/stdio-common/Makefile b/stdio-common/Makefile
> index 96bd7c303a..a10f12ab3c 100644
> --- a/stdio-common/Makefile
> +++ b/stdio-common/Makefile
> @@ -35,7 +35,7 @@ routines	:=							      \
>  	perror psignal							      \
>  	tmpfile tmpfile64 tmpnam tmpnam_r tempnam tempname		      \
>  	getline getw putw						      \
> -	remove rename renameat						      \
> +	remove rename renameat renameat2				      \
>  	flockfile ftrylockfile funlockfile				      \
>  	isoc99_scanf isoc99_vscanf isoc99_fscanf isoc99_vfscanf isoc99_sscanf \
>  	isoc99_vsscanf							      \
> @@ -62,6 +62,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
>  	 tst-vfprintf-user-type \
>  	 tst-vfprintf-mbs-prec \
>  	 tst-scanf-round \
> +	 tst-renameat2 \
>  
>  test-srcs = tst-unbputc tst-printf tst-printfsz-islongdouble
>  
> diff --git a/stdio-common/Versions b/stdio-common/Versions
> index 5016f69c20..b8217578c8 100644
> --- a/stdio-common/Versions
> +++ b/stdio-common/Versions
> @@ -57,6 +57,9 @@ libc {
>      psiginfo;
>      register_printf_modifier; register_printf_type; register_printf_specifier;
>    }
> +  GLIBC_2.28 {
> +    renameat2;
> +  }

Ok.

>    GLIBC_PRIVATE {
>      # global variables
>      _itoa_lower_digits;
> diff --git a/stdio-common/renameat.c b/stdio-common/renameat.c
> index 2180b87bdf..98c8f1d18b 100644
> --- a/stdio-common/renameat.c
> +++ b/stdio-common/renameat.c
> @@ -22,7 +22,7 @@
>  
>  /* Rename the file OLD relative to OLDFD to NEW relative to NEWFD.  */
>  int
> -renameat (int oldfd, const char *old, int newfd, const char *new)
> +__renameat (int oldfd, const char *old, int newfd, const char *new)
>  {
>    if ((oldfd < 0 && oldfd != AT_FDCWD) || (newfd < 0 && newfd != AT_FDCWD))
>      {
> @@ -40,5 +40,6 @@ renameat (int oldfd, const char *old, int newfd, const char *new)
>    return -1;
>  }
>  
> -
> +libc_hidden_def (__renameat)
> +weak_alias (__renameat, renameat)

OK.

>  stub_warning (renameat)
> diff --git a/stdio-common/renameat2.c b/stdio-common/renameat2.c
> new file mode 100644
> index 0000000000..c2cedcd2cb
> --- /dev/null
> +++ b/stdio-common/renameat2.c
> @@ -0,0 +1,30 @@
> +/* Generic implementation of the renameat function.
> +   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 <stdio.h>
> +
> +int
> +renameat2 (int oldfd, const char *old, int newfd, const char *new,
> +           unsigned int flags)
> +{
> +  if (flags == 0)
> +    return __renameat (oldfd, old, newfd, new);
> +  __set_errno (EINVAL);
> +  return -1;
> +}

OK.

> diff --git a/stdio-common/tst-renameat2.c b/stdio-common/tst-renameat2.c
> new file mode 100644
> index 0000000000..958b0918d6
> --- /dev/null
> +++ b/stdio-common/tst-renameat2.c
> @@ -0,0 +1,204 @@
> +/* Linux implementation for renameat2 function.
> +   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 <array_length.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <support/check.h>
> +#include <support/support.h>
> +#include <support/temp_file.h>
> +#include <support/xunistd.h>
> +#include <unistd.h>
> +
> +/* Directory with the temporary files.  */
> +static char *directory;
> +static int directory_fd;
> +
> +/* Paths within that directory.  */
> +static char *old_path;          /* File is called "old".  */
> +static char *new_path;          /* File is called "new".  */
> +
> +/* Subdirectory within the directory above.  */
> +static char *subdirectory;
> +int subdirectory_fd;
> +
> +/* And a pathname in that directory (called "file").  */
> +static char *subdir_path;
> +
> +static void
> +prepare (int argc, char **argv)
> +{
> +  directory = support_create_temp_directory ("tst-renameat2-");
> +  directory_fd = xopen (directory, O_RDONLY | O_DIRECTORY, 0);
> +  old_path = xasprintf ("%s/old", directory);
> +  add_temp_file (old_path);
> +  new_path = xasprintf ("%s/new", directory);
> +  add_temp_file (new_path);
> +  subdirectory = xasprintf ("%s/subdir", directory);
> +  xmkdir (subdirectory, 0777);
> +  add_temp_file (subdirectory);
> +  subdirectory_fd = xopen (subdirectory, O_RDONLY | O_DIRECTORY, 0);
> +  subdir_path = xasprintf ("%s/file", subdirectory);
> +  add_temp_file (subdir_path);
> +}

OK.

> +
> +/* Delete all files, preparing a clean slate for the next test.  */
> +static void
> +delete_all_files (void)
> +{
> +  char *files[] = { old_path, new_path, subdir_path };
> +  for (size_t i = 0; i < array_length (files); ++i)
> +    if (unlink (files[i]) != 0 && errno != ENOENT)
> +      FAIL_EXIT1 ("unlink (\"%s\"): %m", files[i]);
> +}

OK.

> +
> +/* Return true if PATH exists in the file system.  */
> +static bool
> +file_exists (const char *path)
> +{
> +  return access (path, F_OK) == 0;
> +}

OK.

> +
> +/* Check that PATH exists and has size EXPECTED_SIZE.  */
> +static void
> +check_size (const char *path, off64_t expected_size)
> +{
> +  struct stat64 st;
> +  xstat (path, &st);
> +  if (st.st_size != expected_size)
> +    FAIL_EXIT1 ("file \"%s\": expected size %lld, actual size %lld",
> +                path, (unsigned long long int) expected_size,
> +                (unsigned long long int) st.st_size);
> +}

OK.

> +
> +/* Rename tests where the target does not exist.  */
> +static void
> +rename_without_existing_target (unsigned int flags)
> +{
> +  delete_all_files ();
> +  support_write_file_string (old_path, "");
> +  TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, flags), 0);
> +  TEST_VERIFY (!file_exists (old_path));
> +  TEST_VERIFY (file_exists (new_path));
> +
> +  delete_all_files ();
> +  support_write_file_string (old_path, "");
> +  TEST_COMPARE (renameat2 (directory_fd, "old", AT_FDCWD, new_path, flags), 0);
> +  TEST_VERIFY (!file_exists (old_path));
> +  TEST_VERIFY (file_exists (new_path));
> +
> +  delete_all_files ();
> +  support_write_file_string (old_path, "");
> +  TEST_COMPARE (renameat2 (directory_fd, "old", subdirectory_fd, "file", 0),
> +                0);
> +  TEST_VERIFY (!file_exists (old_path));
> +  TEST_VERIFY (file_exists (subdir_path));
> +}

OK.

> +
> +static int
> +do_test (void)
> +{
> +  /* Tests with zero flags argument.  These are expected to succeed
> +     because this renameat2 variant can be implemented with
> +     renameat.  */
> +  rename_without_existing_target (0);
> +
> +  /* renameat2 without flags replaces an existing destination.  */
> +  delete_all_files ();
> +  support_write_file_string (old_path, "123");
> +  support_write_file_string (new_path, "1234");
> +  TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, 0), 0);
> +  TEST_VERIFY (!file_exists (old_path));
> +  check_size (new_path, 3);

OK.

> +
> +  /* Now we need to check for kernel support of renameat2 with
> +     flags.  */
> +  delete_all_files ();
> +  support_write_file_string (old_path, "");
> +  if (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, RENAME_NOREPLACE)
> +      != 0)
> +    {
> +      if (errno == EINVAL)
> +        puts ("warning: no support for renameat2 with flags");
> +      else
> +        FAIL_EXIT1 ("renameat2 probe failed: %m");
> +    }

OK. So we skip renameat2-specific tests, and test only the no-flags test
which fall back to renameat.

> +  else
> +    {
> +      /* We have full renameat2 support.  */
> +      rename_without_existing_target (RENAME_NOREPLACE);
> +
> +      /* Now test RENAME_NOREPLACE with an existing target.  */
> +      delete_all_files ();
> +      support_write_file_string (old_path, "123");
> +      support_write_file_string (new_path, "1234");
> +      TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path,
> +                               RENAME_NOREPLACE), -1);
> +      TEST_COMPARE (errno, EEXIST);
> +      check_size (old_path, 3);
> +      check_size (new_path, 4);
> +
> +      delete_all_files ();
> +      support_write_file_string (old_path, "123");
> +      support_write_file_string (new_path, "1234");
> +      TEST_COMPARE (renameat2 (directory_fd, "old", AT_FDCWD, new_path,
> +                               RENAME_NOREPLACE), -1);
> +      TEST_COMPARE (errno, EEXIST);
> +      check_size (old_path, 3);
> +      check_size (new_path, 4);
> +
> +      delete_all_files ();
> +      support_write_file_string (old_path, "123");
> +      support_write_file_string (subdir_path, "1234");
> +      TEST_COMPARE (renameat2 (directory_fd, "old", subdirectory_fd, "file",
> +                               RENAME_NOREPLACE), -1);
> +      TEST_COMPARE (errno, EEXIST);
> +      check_size (old_path, 3);
> +      check_size (subdir_path, 4);
> +
> +      /* The flag combination of RENAME_NOREPLACE and RENAME_EXCHANGE
> +         is invalid.  */
> +      TEST_COMPARE (renameat2 (directory_fd, "ignored",
> +                               subdirectory_fd, "ignored",
> +                               RENAME_NOREPLACE | RENAME_EXCHANGE), -1);
> +      TEST_COMPARE (errno, EINVAL);
> +    }
> +
> +  /* Create all the pathnames to avoid warnings from the test
> +     harness.  */
> +  support_write_file_string (old_path, "");
> +  support_write_file_string (new_path, "");
> +  support_write_file_string (subdir_path, "");
> +
> +  free (directory);
> +  free (subdirectory);
> +  free (old_path);
> +  free (new_path);
> +  free (subdir_path);
> +
> +  xclose (directory_fd);
> +  xclose (subdirectory_fd);
> +
> +  return 0;
> +}
> +
> +#define PREPARE prepare
> +#include <support/test-driver.c>
> diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
> index 57091fce00..a9089d9064 100644
> --- a/sysdeps/mach/hurd/i386/libc.abilist
> +++ b/sysdeps/mach/hurd/i386/libc.abilist
> @@ -2034,6 +2034,7 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/mach/hurd/renameat.c b/sysdeps/mach/hurd/renameat.c
> index 43609600d9..7985763f73 100644
> --- a/sysdeps/mach/hurd/renameat.c
> +++ b/sysdeps/mach/hurd/renameat.c
> @@ -22,7 +22,7 @@
>  
>  /* Rename the file OLD relative to OLDFD to NEW relative to NEWFD.  */
>  int
> -renameat (int oldfd, const char *old, int newfd, const char *new)
> +__renameat (int oldfd, const char *old, int newfd, const char *new)
>  {
>    error_t err;
>    file_t olddir, newdir;
> @@ -45,3 +45,5 @@ renameat (int oldfd, const char *old, int newfd, const char *new)
>      return __hurd_fail (err);
>    return 0;
>  }
> +libc_hidden_def (__renameat)
> +weak_alias (__renameat, renameat)

OK.

> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> index 884d0dfa95..7a272a19bb 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> @@ -2132,3 +2132,4 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
> diff --git a/sysdeps/unix/sysv/linux/alpha/kernel-features.h b/sysdeps/unix/sysv/linux/alpha/kernel-features.h
> index c2d4a9f55b..5781cffd5a 100644
> --- a/sysdeps/unix/sysv/linux/alpha/kernel-features.h
> +++ b/sysdeps/unix/sysv/linux/alpha/kernel-features.h
> @@ -35,6 +35,11 @@
>  #define __ASSUME_RECV_SYSCALL	1
>  #define __ASSUME_SEND_SYSCALL	1
>  
> +/* Support for the renameat2 syscall was added in 3.17.  */
> +#if __LINUX_KERNEL_VERSION < 0x031100
> +# undef __ASSUME_RENAMEAT2
> +#endif

OK.

> +
>  /* Support for the execveat syscall was added in 4.2.  */
>  #if __LINUX_KERNEL_VERSION < 0x040200
>  # undef __ASSUME_EXECVEAT
> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> index 28d54b9794..23fec55d97 100644
> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> @@ -2027,6 +2027,7 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
> index dfde3bd725..b203160889 100644
> --- a/sysdeps/unix/sysv/linux/arm/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
> @@ -117,6 +117,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.4 _Exit F
>  GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
>  GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
> diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> index 06b00f730a..64809ca403 100644
> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> @@ -1874,6 +1874,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
> index 1c1cc00d40..4a87f62ad9 100644
> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
> @@ -2039,6 +2039,7 @@ GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> index f6e17a005f..6bdd0d0443 100644
> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> @@ -1908,6 +1908,7 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
> index b90ea30195..86fc66574b 100644
> --- a/sysdeps/unix/sysv/linux/kernel-features.h
> +++ b/sysdeps/unix/sysv/linux/kernel-features.h
> @@ -97,6 +97,11 @@
>     implementation based on p{read,write}v and returning an error for
>     non supported flags.  */
>  
> +/* Support for the renameat2 system call was added in kernel 3.15.  */
> +#if __LINUX_KERNEL_VERSION >= 0x030F00
> +# define __ASSUME_RENAMEAT2
> +#endif

OK.

> +
>  /* Support for the execveat syscall was added in 3.19.  */
>  #if __LINUX_KERNEL_VERSION >= 0x031300
>  # define __ASSUME_EXECVEAT	1
> diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> index ee054a618d..3226f916eb 100644
> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> @@ -118,6 +118,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.4 _Exit F
>  GLIBC_2.4 _IO_2_1_stderr_ D 0x98
>  GLIBC_2.4 _IO_2_1_stdin_ D 0x98
> diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> index 227a0581cb..b1074eeed1 100644
> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> @@ -1983,6 +1983,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/microblaze/kernel-features.h b/sysdeps/unix/sysv/linux/microblaze/kernel-features.h
> index b13b863f06..7db3d05636 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/kernel-features.h
> +++ b/sysdeps/unix/sysv/linux/microblaze/kernel-features.h
> @@ -48,6 +48,11 @@
>  # undef __ASSUME_SENDMMSG_SYSCALL
>  #endif
>  
> +/* Support for the renameat2 syscall was added in 3.17.  */
> +#if __LINUX_KERNEL_VERSION < 0x031100
> +# undef __ASSUME_RENAMEAT2
> +#endif

OK.

> +
>  /* Support for the execveat syscall was added in 4.0.  */
>  #if __LINUX_KERNEL_VERSION < 0x040000
>  # undef __ASSUME_EXECVEAT
> diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> index 18781b3017..b52fc7d6f7 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> @@ -2124,3 +2124,4 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> index 2d86989cf2..718c74262a 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> @@ -1961,6 +1961,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> index b8b113e1a5..9b218a9435 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> @@ -1959,6 +1959,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> index 6a3cd13e2d..5a90ab83e7 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> @@ -1967,6 +1967,7 @@ GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> index 596ec05379..3005fc9500 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> @@ -1962,6 +1962,7 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> index 8da18eed57..a87fbbeb6b 100644
> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> @@ -2165,3 +2165,4 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> index 555751eb12..d56f776a52 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> @@ -1987,6 +1987,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> index 80324e41aa..2b5337aab6 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> @@ -1991,6 +1991,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> index 97b1d354af..d0dfde3897 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> @@ -2222,3 +2222,4 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> index 15be314921..d505ae0e7d 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> @@ -117,6 +117,7 @@ GLIBC_2.27 wcstof32x_l F
>  GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 _Exit F
>  GLIBC_2.3 _IO_2_1_stderr_ D 0xe0
>  GLIBC_2.3 _IO_2_1_stdin_ D 0xe0
> diff --git a/sysdeps/unix/sysv/linux/renameat.c b/sysdeps/unix/sysv/linux/renameat.c
> index 034432b934..f85c5ae0ec 100644
> --- a/sysdeps/unix/sysv/linux/renameat.c
> +++ b/sysdeps/unix/sysv/linux/renameat.c
> @@ -22,7 +22,7 @@
>  #include <errno.h>
>  
>  int
> -renameat (int oldfd, const char *old, int newfd, const char *new)
> +__renameat (int oldfd, const char *old, int newfd, const char *new)
>  {
>  #ifdef __NR_renameat
>    return INLINE_SYSCALL_CALL (renameat, oldfd, old, newfd, new);
> @@ -30,3 +30,5 @@ renameat (int oldfd, const char *old, int newfd, const char *new)
>    return INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, 0);
>  #endif
>  }
> +libc_hidden_def (__renameat)
> +weak_alias (__renameat, renameat)

OK.

> diff --git a/sysdeps/unix/sysv/linux/renameat2.c b/sysdeps/unix/sysv/linux/renameat2.c
> new file mode 100644
> index 0000000000..919bb2a0d4
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/renameat2.c
> @@ -0,0 +1,44 @@
> +/* Linux implementation for renameat2 function.
> +   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 <stdio.h>
> +#include <sysdep.h>
> +
> +int
> +renameat2 (int oldfd, const char *old, int newfd, const char *new,
> +           unsigned int flags)
> +{
> +#if !defined (__NR_renameat) || defined (__ASSUME_RENAMEAT2)
> +  return INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, flags);
> +#else
> +  if (flags == 0)
> +    return __renameat (oldfd, old, newfd, new);

OK. Use renameat.

> +# ifdef __NR_renameat2
> +  /* For non-zero flags, try the renameat2 system call.  */
> +  int ret = INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, flags);
> +  if (ret != -1 || errno != ENOSYS)
> +    /* Preserve non-error/non-ENOSYS return values.  */
> +    return ret;

OK.

> +# endif
> +  /* No kernel (header) support for renameat2.  All flags are
> +     unknown.  */
> +  __set_errno (EINVAL);
> +  return -1;

OK. Fallback.

> +#endif
> +}

OK.

> diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> index 436b992251..33f751abc0 100644
> --- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> @@ -2094,3 +2094,4 @@ GLIBC_2.27 xencrypt F
>  GLIBC_2.27 xprt_register F
>  GLIBC_2.27 xprt_unregister F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> index f66715f0c9..c4ec93ff43 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> @@ -1996,6 +1996,7 @@ GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> index bd6242861b..2a6a0abde8 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> @@ -1901,6 +1901,7 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/sh/kernel-features.h b/sysdeps/unix/sysv/linux/sh/kernel-features.h
> index b82d032e6b..05b7dcd037 100644
> --- a/sysdeps/unix/sysv/linux/sh/kernel-features.h
> +++ b/sysdeps/unix/sysv/linux/sh/kernel-features.h
> @@ -51,4 +51,9 @@
>  /* sh only supports ipc syscall.  */
>  #undef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
>  
> +/* Support for the renameat2 syscall was added in 4.8.  */
> +#if __LINUX_KERNEL_VERSION < 0x040800

OK.

> +# undef __ASSUME_RENAMEAT2
> +#endif
> +
>  #endif
> diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
> index f2f070fbce..8de0d1711a 100644
> --- a/sysdeps/unix/sysv/linux/sh/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
> @@ -1878,6 +1878,7 @@ GLIBC_2.27 wcstof64 F
>  GLIBC_2.27 wcstof64_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/sparc/kernel-features.h b/sysdeps/unix/sysv/linux/sparc/kernel-features.h
> index 64d714036e..91990a716f 100644
> --- a/sysdeps/unix/sysv/linux/sparc/kernel-features.h
> +++ b/sysdeps/unix/sysv/linux/sparc/kernel-features.h
> @@ -41,6 +41,11 @@
>  /* sparc only supports ipc syscall.  */
>  #undef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
>  
> +/* Support for the renameat2 syscall was added in 3.16.  */
> +#if __LINUX_KERNEL_VERSION < 0x031000

OK.

> +# undef __ASSUME_RENAMEAT2
> +#endif
> +
>  /* SPARC kernel Kconfig does not define CONFIG_CLONE_BACKWARDS, however it
>     has the same ABI as if it did, implemented by sparc-specific code
>     (sparc_do_fork).
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> index 265087f6a8..9858460c9f 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> @@ -1990,6 +1990,7 @@ GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> index 16a69812cb..a22b8fb7ca 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> @@ -1931,6 +1931,7 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> index fa8c198d13..d5d71cccba 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> @@ -1889,6 +1889,7 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F
>  GLIBC_2.3 __ctype_b_loc F
>  GLIBC_2.3 __ctype_tolower_loc F
>  GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> index 2536971682..e6ad42440e 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> @@ -2140,3 +2140,4 @@ GLIBC_2.27 wcstof64_l F
>  GLIBC_2.27 wcstof64x F
>  GLIBC_2.27 wcstof64x_l F
>  GLIBC_2.28 fcntl64 F
> +GLIBC_2.28 renameat2 F

OK. libc.abilist changes look fine.
diff mbox series

Patch

diff --git a/NEWS b/NEWS
index a27dd371a3..093f364c7e 100644
--- a/NEWS
+++ b/NEWS
@@ -35,6 +35,8 @@  Major new features:
 * Building and running on GNU/Hurd systems now works without out-of-tree
   patches.
 
+* The renameat2 function has been added.
+
 * IDN domain names in getaddrinfo and getnameinfo now use the system libidn2
   library if installed.  libidn2 version 2.0.5 or later is recommended.  If
   libidn2 is not available, internationalized domain names are not encoded
diff --git a/include/stdio.h b/include/stdio.h
index f140813ad6..3ba0edc924 100644
--- a/include/stdio.h
+++ b/include/stdio.h
@@ -237,5 +237,8 @@  __putc_unlocked (int __c, FILE *__stream)
 }
 #  endif
 
+extern __typeof (renameat) __renameat;
+libc_hidden_proto (__renameat)
+
 # endif /* not _ISOMAC */
 #endif /* stdio.h */
diff --git a/libio/stdio.h b/libio/stdio.h
index 731f8e56f4..3cdc8cc532 100644
--- a/libio/stdio.h
+++ b/libio/stdio.h
@@ -153,6 +153,18 @@  extern int renameat (int __oldfd, const char *__old, int __newfd,
 		     const char *__new) __THROW;
 #endif
 
+#ifdef __USE_GNU
+/* Flags for renameat.  */
+# define RENAME_NOREPLACE (1 << 0)
+# define RENAME_EXCHANGE (1 << 1)
+# define RENAME_WHITEOUT (1 << 2)
+
+/* Rename file OLD relative to OLDFD to NEW relative to NEWFD, with
+   additional flags.  */
+extern int renameat2 (int __oldfd, const char *__old, int __newfd,
+		      const char *__new, unsigned int __flags) __THROW;
+#endif
+
 /* Create a temporary file and open it read/write.
 
    This function is a possible cancellation point and therefore not
diff --git a/manual/filesys.texi b/manual/filesys.texi
index cc70a6b7ee..db2f1041de 100644
--- a/manual/filesys.texi
+++ b/manual/filesys.texi
@@ -3552,6 +3552,7 @@  The @code{mkdtemp} function comes from OpenBSD.
 @c open_by_handle_at
 @c readlinkat
 @c renameat
+@c renameat2
 @c scandirat
 @c symlinkat
 @c unlinkat
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 738a3cead0..6fd628708f 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -35,7 +35,7 @@  routines	:=							      \
 	perror psignal							      \
 	tmpfile tmpfile64 tmpnam tmpnam_r tempnam tempname		      \
 	getline getw putw						      \
-	remove rename renameat						      \
+	remove rename renameat renameat2				      \
 	flockfile ftrylockfile funlockfile				      \
 	isoc99_scanf isoc99_vscanf isoc99_fscanf isoc99_vfscanf isoc99_sscanf \
 	isoc99_vsscanf							      \
@@ -62,6 +62,7 @@  tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
 	 tst-vfprintf-user-type \
 	 tst-vfprintf-mbs-prec \
 	 tst-scanf-round \
+	 tst-renameat2 \
 
 test-srcs = tst-unbputc tst-printf
 
diff --git a/stdio-common/Versions b/stdio-common/Versions
index 5016f69c20..b8217578c8 100644
--- a/stdio-common/Versions
+++ b/stdio-common/Versions
@@ -57,6 +57,9 @@  libc {
     psiginfo;
     register_printf_modifier; register_printf_type; register_printf_specifier;
   }
+  GLIBC_2.28 {
+    renameat2;
+  }
   GLIBC_PRIVATE {
     # global variables
     _itoa_lower_digits;
diff --git a/stdio-common/renameat.c b/stdio-common/renameat.c
index 2180b87bdf..98c8f1d18b 100644
--- a/stdio-common/renameat.c
+++ b/stdio-common/renameat.c
@@ -22,7 +22,7 @@ 
 
 /* Rename the file OLD relative to OLDFD to NEW relative to NEWFD.  */
 int
-renameat (int oldfd, const char *old, int newfd, const char *new)
+__renameat (int oldfd, const char *old, int newfd, const char *new)
 {
   if ((oldfd < 0 && oldfd != AT_FDCWD) || (newfd < 0 && newfd != AT_FDCWD))
     {
@@ -40,5 +40,6 @@  renameat (int oldfd, const char *old, int newfd, const char *new)
   return -1;
 }
 
-
+libc_hidden_def (__renameat)
+weak_alias (__renameat, renameat)
 stub_warning (renameat)
diff --git a/stdio-common/renameat2.c b/stdio-common/renameat2.c
new file mode 100644
index 0000000000..c2cedcd2cb
--- /dev/null
+++ b/stdio-common/renameat2.c
@@ -0,0 +1,30 @@ 
+/* Generic implementation of the renameat function.
+   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 <stdio.h>
+
+int
+renameat2 (int oldfd, const char *old, int newfd, const char *new,
+           unsigned int flags)
+{
+  if (flags == 0)
+    return __renameat (oldfd, old, newfd, new);
+  __set_errno (EINVAL);
+  return -1;
+}
diff --git a/stdio-common/tst-renameat2.c b/stdio-common/tst-renameat2.c
new file mode 100644
index 0000000000..958b0918d6
--- /dev/null
+++ b/stdio-common/tst-renameat2.c
@@ -0,0 +1,204 @@ 
+/* Linux implementation for renameat2 function.
+   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 <array_length.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/xunistd.h>
+#include <unistd.h>
+
+/* Directory with the temporary files.  */
+static char *directory;
+static int directory_fd;
+
+/* Paths within that directory.  */
+static char *old_path;          /* File is called "old".  */
+static char *new_path;          /* File is called "new".  */
+
+/* Subdirectory within the directory above.  */
+static char *subdirectory;
+int subdirectory_fd;
+
+/* And a pathname in that directory (called "file").  */
+static char *subdir_path;
+
+static void
+prepare (int argc, char **argv)
+{
+  directory = support_create_temp_directory ("tst-renameat2-");
+  directory_fd = xopen (directory, O_RDONLY | O_DIRECTORY, 0);
+  old_path = xasprintf ("%s/old", directory);
+  add_temp_file (old_path);
+  new_path = xasprintf ("%s/new", directory);
+  add_temp_file (new_path);
+  subdirectory = xasprintf ("%s/subdir", directory);
+  xmkdir (subdirectory, 0777);
+  add_temp_file (subdirectory);
+  subdirectory_fd = xopen (subdirectory, O_RDONLY | O_DIRECTORY, 0);
+  subdir_path = xasprintf ("%s/file", subdirectory);
+  add_temp_file (subdir_path);
+}
+
+/* Delete all files, preparing a clean slate for the next test.  */
+static void
+delete_all_files (void)
+{
+  char *files[] = { old_path, new_path, subdir_path };
+  for (size_t i = 0; i < array_length (files); ++i)
+    if (unlink (files[i]) != 0 && errno != ENOENT)
+      FAIL_EXIT1 ("unlink (\"%s\"): %m", files[i]);
+}
+
+/* Return true if PATH exists in the file system.  */
+static bool
+file_exists (const char *path)
+{
+  return access (path, F_OK) == 0;
+}
+
+/* Check that PATH exists and has size EXPECTED_SIZE.  */
+static void
+check_size (const char *path, off64_t expected_size)
+{
+  struct stat64 st;
+  xstat (path, &st);
+  if (st.st_size != expected_size)
+    FAIL_EXIT1 ("file \"%s\": expected size %lld, actual size %lld",
+                path, (unsigned long long int) expected_size,
+                (unsigned long long int) st.st_size);
+}
+
+/* Rename tests where the target does not exist.  */
+static void
+rename_without_existing_target (unsigned int flags)
+{
+  delete_all_files ();
+  support_write_file_string (old_path, "");
+  TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, flags), 0);
+  TEST_VERIFY (!file_exists (old_path));
+  TEST_VERIFY (file_exists (new_path));
+
+  delete_all_files ();
+  support_write_file_string (old_path, "");
+  TEST_COMPARE (renameat2 (directory_fd, "old", AT_FDCWD, new_path, flags), 0);
+  TEST_VERIFY (!file_exists (old_path));
+  TEST_VERIFY (file_exists (new_path));
+
+  delete_all_files ();
+  support_write_file_string (old_path, "");
+  TEST_COMPARE (renameat2 (directory_fd, "old", subdirectory_fd, "file", 0),
+                0);
+  TEST_VERIFY (!file_exists (old_path));
+  TEST_VERIFY (file_exists (subdir_path));
+}
+
+static int
+do_test (void)
+{
+  /* Tests with zero flags argument.  These are expected to succeed
+     because this renameat2 variant can be implemented with
+     renameat.  */
+  rename_without_existing_target (0);
+
+  /* renameat2 without flags replaces an existing destination.  */
+  delete_all_files ();
+  support_write_file_string (old_path, "123");
+  support_write_file_string (new_path, "1234");
+  TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, 0), 0);
+  TEST_VERIFY (!file_exists (old_path));
+  check_size (new_path, 3);
+
+  /* Now we need to check for kernel support of renameat2 with
+     flags.  */
+  delete_all_files ();
+  support_write_file_string (old_path, "");
+  if (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, RENAME_NOREPLACE)
+      != 0)
+    {
+      if (errno == EINVAL)
+        puts ("warning: no support for renameat2 with flags");
+      else
+        FAIL_EXIT1 ("renameat2 probe failed: %m");
+    }
+  else
+    {
+      /* We have full renameat2 support.  */
+      rename_without_existing_target (RENAME_NOREPLACE);
+
+      /* Now test RENAME_NOREPLACE with an existing target.  */
+      delete_all_files ();
+      support_write_file_string (old_path, "123");
+      support_write_file_string (new_path, "1234");
+      TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path,
+                               RENAME_NOREPLACE), -1);
+      TEST_COMPARE (errno, EEXIST);
+      check_size (old_path, 3);
+      check_size (new_path, 4);
+
+      delete_all_files ();
+      support_write_file_string (old_path, "123");
+      support_write_file_string (new_path, "1234");
+      TEST_COMPARE (renameat2 (directory_fd, "old", AT_FDCWD, new_path,
+                               RENAME_NOREPLACE), -1);
+      TEST_COMPARE (errno, EEXIST);
+      check_size (old_path, 3);
+      check_size (new_path, 4);
+
+      delete_all_files ();
+      support_write_file_string (old_path, "123");
+      support_write_file_string (subdir_path, "1234");
+      TEST_COMPARE (renameat2 (directory_fd, "old", subdirectory_fd, "file",
+                               RENAME_NOREPLACE), -1);
+      TEST_COMPARE (errno, EEXIST);
+      check_size (old_path, 3);
+      check_size (subdir_path, 4);
+
+      /* The flag combination of RENAME_NOREPLACE and RENAME_EXCHANGE
+         is invalid.  */
+      TEST_COMPARE (renameat2 (directory_fd, "ignored",
+                               subdirectory_fd, "ignored",
+                               RENAME_NOREPLACE | RENAME_EXCHANGE), -1);
+      TEST_COMPARE (errno, EINVAL);
+    }
+
+  /* Create all the pathnames to avoid warnings from the test
+     harness.  */
+  support_write_file_string (old_path, "");
+  support_write_file_string (new_path, "");
+  support_write_file_string (subdir_path, "");
+
+  free (directory);
+  free (subdirectory);
+  free (old_path);
+  free (new_path);
+  free (subdir_path);
+
+  xclose (directory_fd);
+  xclose (subdirectory_fd);
+
+  return 0;
+}
+
+#define PREPARE prepare
+#include <support/test-driver.c>
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index 3d46de795d..9df86e491f 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2035,6 +2035,7 @@  GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/mach/hurd/renameat.c b/sysdeps/mach/hurd/renameat.c
index 43609600d9..7985763f73 100644
--- a/sysdeps/mach/hurd/renameat.c
+++ b/sysdeps/mach/hurd/renameat.c
@@ -22,7 +22,7 @@ 
 
 /* Rename the file OLD relative to OLDFD to NEW relative to NEWFD.  */
 int
-renameat (int oldfd, const char *old, int newfd, const char *new)
+__renameat (int oldfd, const char *old, int newfd, const char *new)
 {
   error_t err;
   file_t olddir, newdir;
@@ -45,3 +45,5 @@  renameat (int oldfd, const char *old, int newfd, const char *new)
     return __hurd_fail (err);
   return 0;
 }
+libc_hidden_def (__renameat)
+weak_alias (__renameat, renameat)
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 884d0dfa95..7a272a19bb 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2132,3 +2132,4 @@  GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 28d54b9794..23fec55d97 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2027,6 +2027,7 @@  GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index dfde3bd725..b203160889 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -117,6 +117,7 @@  GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 06b00f730a..64809ca403 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -1874,6 +1874,7 @@  GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 1c1cc00d40..4a87f62ad9 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2039,6 +2039,7 @@  GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index f6e17a005f..6bdd0d0443 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -1908,6 +1908,7 @@  GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
index b90ea30195..ab42c5845a 100644
--- a/sysdeps/unix/sysv/linux/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/kernel-features.h
@@ -144,3 +144,8 @@ 
    */
 
 #define __ASSUME_CLONE_DEFAULT 1
+
+/* Support for the renameat2 system call was added in kernel 3.15.  */
+#if __LINUX_KERNEL_VERSION >= 0x030F00
+# define __ASSUME_RENAMEAT2
+#endif
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index ee054a618d..3226f916eb 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -118,6 +118,7 @@  GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0x98
 GLIBC_2.4 _IO_2_1_stdin_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 227a0581cb..b1074eeed1 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -1983,6 +1983,7 @@  GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index 18781b3017..b52fc7d6f7 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2124,3 +2124,4 @@  GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 2d86989cf2..718c74262a 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -1961,6 +1961,7 @@  GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index b8b113e1a5..9b218a9435 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -1959,6 +1959,7 @@  GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 6a3cd13e2d..5a90ab83e7 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -1967,6 +1967,7 @@  GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 596ec05379..3005fc9500 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -1962,6 +1962,7 @@  GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 8da18eed57..a87fbbeb6b 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2165,3 +2165,4 @@  GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 555751eb12..d56f776a52 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -1987,6 +1987,7 @@  GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index 80324e41aa..2b5337aab6 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -1991,6 +1991,7 @@  GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
index 97b1d354af..d0dfde3897 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
@@ -2222,3 +2222,4 @@  GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
index 15be314921..d505ae0e7d 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
@@ -117,6 +117,7 @@  GLIBC_2.27 wcstof32x_l F
 GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 _Exit F
 GLIBC_2.3 _IO_2_1_stderr_ D 0xe0
 GLIBC_2.3 _IO_2_1_stdin_ D 0xe0
diff --git a/sysdeps/unix/sysv/linux/renameat.c b/sysdeps/unix/sysv/linux/renameat.c
index 034432b934..f85c5ae0ec 100644
--- a/sysdeps/unix/sysv/linux/renameat.c
+++ b/sysdeps/unix/sysv/linux/renameat.c
@@ -22,7 +22,7 @@ 
 #include <errno.h>
 
 int
-renameat (int oldfd, const char *old, int newfd, const char *new)
+__renameat (int oldfd, const char *old, int newfd, const char *new)
 {
 #ifdef __NR_renameat
   return INLINE_SYSCALL_CALL (renameat, oldfd, old, newfd, new);
@@ -30,3 +30,5 @@  renameat (int oldfd, const char *old, int newfd, const char *new)
   return INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, 0);
 #endif
 }
+libc_hidden_def (__renameat)
+weak_alias (__renameat, renameat)
diff --git a/sysdeps/unix/sysv/linux/renameat2.c b/sysdeps/unix/sysv/linux/renameat2.c
new file mode 100644
index 0000000000..919bb2a0d4
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/renameat2.c
@@ -0,0 +1,44 @@ 
+/* Linux implementation for renameat2 function.
+   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 <stdio.h>
+#include <sysdep.h>
+
+int
+renameat2 (int oldfd, const char *old, int newfd, const char *new,
+           unsigned int flags)
+{
+#if !defined (__NR_renameat) || defined (__ASSUME_RENAMEAT2)
+  return INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, flags);
+#else
+  if (flags == 0)
+    return __renameat (oldfd, old, newfd, new);
+# ifdef __NR_renameat2
+  /* For non-zero flags, try the renameat2 system call.  */
+  int ret = INLINE_SYSCALL_CALL (renameat2, oldfd, old, newfd, new, flags);
+  if (ret != -1 || errno != ENOSYS)
+    /* Preserve non-error/non-ENOSYS return values.  */
+    return ret;
+# endif
+  /* No kernel (header) support for renameat2.  All flags are
+     unknown.  */
+  __set_errno (EINVAL);
+  return -1;
+#endif
+}
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 436b992251..33f751abc0 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2094,3 +2094,4 @@  GLIBC_2.27 xencrypt F
 GLIBC_2.27 xprt_register F
 GLIBC_2.27 xprt_unregister F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index f66715f0c9..c4ec93ff43 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -1996,6 +1996,7 @@  GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index bd6242861b..2a6a0abde8 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -1901,6 +1901,7 @@  GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index f2f070fbce..8de0d1711a 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -1878,6 +1878,7 @@  GLIBC_2.27 wcstof64 F
 GLIBC_2.27 wcstof64_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 265087f6a8..9858460c9f 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -1990,6 +1990,7 @@  GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 16a69812cb..a22b8fb7ca 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -1931,6 +1931,7 @@  GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index fa8c198d13..d5d71cccba 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1889,6 +1889,7 @@  GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
 GLIBC_2.3 __ctype_toupper_loc F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 2536971682..e6ad42440e 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2140,3 +2140,4 @@  GLIBC_2.27 wcstof64_l F
 GLIBC_2.27 wcstof64x F
 GLIBC_2.27 wcstof64x_l F
 GLIBC_2.28 fcntl64 F
+GLIBC_2.28 renameat2 F