Add renameat2.

Bug: http://b/127675384
Test: new tests
Change-Id: Ia2e3d5679180391ca98e62fa429fa11cbf167507
diff --git a/docs/status.md b/docs/status.md
index 5c2f2e8..2f356f3 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -39,6 +39,7 @@
 
 New libc functions in R (API level 30):
   * Full C11 `<threads.h>` (available as inlines for older API levels).
+  * `renameat2` (GNU extension).
 
 New libc functions in Q (API level 29):
   * `timespec_get` (C11 `<time.h>` addition)
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index 99bdc93..62d698f 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -151,6 +151,7 @@
 int mknodat(int, const char*, mode_t, dev_t)  all
 int readlinkat(int, const char*, char*, size_t)  all
 int renameat(int, const char*, int, const char*)  all
+int renameat2(int, const char*, int, const char*, unsigned)  all
 int symlinkat(const char*, int, const char*)  all
 int unlinkat(int, const char*, int)   all
 int utimensat(int, const char*, const struct timespec times[2], int)  all
diff --git a/libc/include/stdio.h b/libc/include/stdio.h
index 8bd690f..6632c01 100644
--- a/libc/include/stdio.h
+++ b/libc/include/stdio.h
@@ -165,9 +165,53 @@
 char* tempnam(const char* __dir, const char* __prefix)
     __warnattr("tempnam is unsafe, use mkstemp or tmpfile instead");
 
+/**
+ * [rename(2)](http://man7.org/linux/man-pages/man2/rename.2.html) changes
+ * the name or location of a file.
+ *
+ * Returns 0 on success, and returns -1 and sets `errno` on failure.
+ */
 int rename(const char* __old_path, const char* __new_path);
+
+/**
+ * [renameat(2)](http://man7.org/linux/man-pages/man2/renameat.2.html) changes
+ * the name or location of a file, interpreting relative paths using an fd.
+ *
+ * Returns 0 on success, and returns -1 and sets `errno` on failure.
+ */
 int renameat(int __old_dir_fd, const char* __old_path, int __new_dir_fd, const char* __new_path);
 
+#if defined(__USE_GNU)
+
+/**
+ * Flag for [renameat2(2)](http://man7.org/linux/man-pages/man2/renameat2.2.html)
+ * to fail if the new path already exists.
+ */
+#define RENAME_NOREPLACE (1<<0)
+
+/**
+ * Flag for [renameat2(2)](http://man7.org/linux/man-pages/man2/renameat2.2.html)
+ * to atomically exchange the two paths.
+ */
+#define RENAME_EXCHANGE (1<<1)
+
+/**
+ * Flag for [renameat2(2)](http://man7.org/linux/man-pages/man2/renameat2.2.html)
+ * to create a union/overlay filesystem object.
+ */
+#define RENAME_WHITEOUT (1<<2)
+
+/**
+ * [renameat2(2)](http://man7.org/linux/man-pages/man2/renameat2.2.html) changes
+ * the name or location of a file, interpreting relative paths using an fd,
+ * with optional `RENAME_` flags.
+ *
+ * Returns 0 on success, and returns -1 and sets `errno` on failure.
+ */
+int renameat2(int __old_dir_fd, const char* __old_path, int __new_dir_fd, const char* __new_path, unsigned __flags) __INTRODUCED_IN(30);
+
+#endif
+
 int fseek(FILE* __fp, long __offset, int __whence);
 long ftell(FILE* __fp);
 
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 0cab83d..c3b9e2c 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1503,6 +1503,7 @@
     pthread_mutex_clocklock;
     pthread_rwlock_clockrdlock;
     pthread_rwlock_clockwrlock;
+    renameat2;
     sem_clockwait;
     thrd_create;
     thrd_current;
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 01b4dba..a0cda1b 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -19,7 +19,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
-#include <linux/fs.h>
 #include <math.h>
 #include <stdio.h>
 #include <sys/types.h>
@@ -34,10 +33,18 @@
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/unique_fd.h>
 
 #include "BionicDeathTest.h"
 #include "utils.h"
 
+// This #include is actually a test too. We have to duplicate the
+// definitions of the RENAME_ constants because <linux/fs.h> also contains
+// pollution such as BLOCK_SIZE which conflicts with lots of user code.
+// Important to check that we have matching definitions.
+// There's no _MAX to test that we have all the constants, sadly.
+#include <linux/fs.h>
+
 #if defined(NOFORTIFY)
 #define STDIO_TEST stdio_nofortify
 #define STDIO_DEATHTEST stdio_nofortify_DeathTest
@@ -2610,3 +2617,77 @@
   // So we'll notice if Linux grows another constant in <linux/fs.h>...
   ASSERT_EQ(SEEK_MAX, SEEK_HOLE);
 }
+
+TEST(STDIO_TEST, rename) {
+  TemporaryDir td;
+  std::string old_path = td.path + "/old"s;
+  std::string new_path = td.path + "/new"s;
+
+  // Create the file, check it exists.
+  ASSERT_EQ(0, close(creat(old_path.c_str(), 0666)));
+  struct stat sb;
+  ASSERT_EQ(0, stat(old_path.c_str(), &sb));
+  ASSERT_EQ(-1, stat(new_path.c_str(), &sb));
+
+  // Rename and check it moved.
+  ASSERT_EQ(0, rename(old_path.c_str(), new_path.c_str()));
+  ASSERT_EQ(-1, stat(old_path.c_str(), &sb));
+  ASSERT_EQ(0, stat(new_path.c_str(), &sb));
+}
+
+TEST(STDIO_TEST, renameat) {
+  TemporaryDir td;
+  android::base::unique_fd dirfd{open(td.path, O_PATH)};
+  std::string old_path = td.path + "/old"s;
+  std::string new_path = td.path + "/new"s;
+
+  // Create the file, check it exists.
+  ASSERT_EQ(0, close(creat(old_path.c_str(), 0666)));
+  struct stat sb;
+  ASSERT_EQ(0, stat(old_path.c_str(), &sb));
+  ASSERT_EQ(-1, stat(new_path.c_str(), &sb));
+
+  // Rename and check it moved.
+  ASSERT_EQ(0, renameat(dirfd, "old", dirfd, "new"));
+  ASSERT_EQ(-1, stat(old_path.c_str(), &sb));
+  ASSERT_EQ(0, stat(new_path.c_str(), &sb));
+}
+
+TEST(STDIO_TEST, renameat2) {
+#if defined(__GLIBC__)
+  GTEST_SKIP() << "glibc doesn't have renameat2 until 2.28";
+#else
+  TemporaryDir td;
+  android::base::unique_fd dirfd{open(td.path, O_PATH)};
+  std::string old_path = td.path + "/old"s;
+  std::string new_path = td.path + "/new"s;
+
+  // Create the file, check it exists.
+  ASSERT_EQ(0, close(creat(old_path.c_str(), 0666)));
+  struct stat sb;
+  ASSERT_EQ(0, stat(old_path.c_str(), &sb));
+  ASSERT_EQ(-1, stat(new_path.c_str(), &sb));
+
+  // Rename and check it moved.
+  ASSERT_EQ(0, renameat2(dirfd, "old", dirfd, "new", 0));
+  ASSERT_EQ(-1, stat(old_path.c_str(), &sb));
+  ASSERT_EQ(0, stat(new_path.c_str(), &sb));
+
+  // After this, both "old" and "new" exist.
+  ASSERT_EQ(0, close(creat(old_path.c_str(), 0666)));
+
+  // Rename and check it moved.
+  ASSERT_EQ(-1, renameat2(dirfd, "old", dirfd, "new", RENAME_NOREPLACE));
+  ASSERT_EQ(EEXIST, errno);
+#endif
+}
+
+TEST(STDIO_TEST, renameat2_flags) {
+#if defined(__GLIBC__)
+  GTEST_SKIP() << "glibc doesn't have renameat2 until 2.28";
+#else
+ ASSERT_NE(0, RENAME_EXCHANGE);
+ ASSERT_NE(0, RENAME_NOREPLACE);
+ ASSERT_NE(0, RENAME_WHITEOUT);
+#endif
+}