Add renameat2.

Bug: http://b/127675384
Test: new tests
Change-Id: Ia2e3d5679180391ca98e62fa429fa11cbf167507
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
+}