Reimplement realpath.

Use O_PATH like musl to let the kernel do the hard work, rather than the
traditional BSD manual scheme.

Also add the most obvious missing tests from reading the man page, plus
a non-obvious test for deleted files.

Bug: http://b/131435126
Test: treehugger
Change-Id: Ie8a8986fea55f045952a81afee377ce8288a49d5
diff --git a/tests/stdlib_test.cpp b/tests/stdlib_test.cpp
index ff4cb71..c522fff 100644
--- a/tests/stdlib_test.cpp
+++ b/tests/stdlib_test.cpp
@@ -29,6 +29,7 @@
 #include <limits>
 #include <string>
 
+#include <android-base/file.h>
 #include <android-base/macros.h>
 #include <gtest/gtest.h>
 
@@ -36,6 +37,8 @@
 #include "math_data_test.h"
 #include "utils.h"
 
+using namespace std::string_literals;
+
 template <typename T = int (*)(char*)>
 class GenericTemporaryFile {
  public:
@@ -322,6 +325,17 @@
   ASSERT_EQ(ENOENT, errno);
 }
 
+TEST(stdlib, realpath__ELOOP) {
+  TemporaryDir td;
+  std::string link = std::string(td.path) + "/loop";
+  ASSERT_EQ(0, symlink(link.c_str(), link.c_str()));
+
+  errno = 0;
+  char* p = realpath(link.c_str(), nullptr);
+  ASSERT_TRUE(p == nullptr);
+  ASSERT_EQ(ELOOP, errno);
+}
+
 TEST(stdlib, realpath__component_after_non_directory) {
   errno = 0;
   char* p = realpath("/dev/null/.", nullptr);
@@ -350,6 +364,47 @@
   free(p);
 }
 
+TEST(stdlib, realpath__dot) {
+  char* p = realpath("/proc/./version", nullptr);
+  ASSERT_STREQ("/proc/version", p);
+  free(p);
+}
+
+TEST(stdlib, realpath__dot_dot) {
+  char* p = realpath("/dev/../proc/version", nullptr);
+  ASSERT_STREQ("/proc/version", p);
+  free(p);
+}
+
+TEST(stdlib, realpath__deleted) {
+  TemporaryDir td;
+
+  // Create a file "A".
+  std::string A_path = td.path + "/A"s;
+  ASSERT_TRUE(android::base::WriteStringToFile("test\n", A_path));
+
+  // Get an O_PATH fd for it.
+  android::base::unique_fd fd(open(A_path.c_str(), O_PATH));
+  ASSERT_NE(fd, -1);
+
+  // Create a file "A (deleted)".
+  android::base::unique_fd fd2(open((td.path + "/A (deleted)"s).c_str(),
+                                    O_CREAT | O_TRUNC | O_WRONLY, 0644));
+  ASSERT_NE(fd2, -1);
+
+  // Delete "A".
+  ASSERT_EQ(0, unlink(A_path.c_str()));
+
+  // Now realpath() on the O_PATH fd, and check we *don't* get "A (deleted)".
+  std::string path = android::base::StringPrintf("/proc/%d/fd/%d", static_cast<int>(getpid()),
+                                                 fd.get());
+  errno = 0;
+  char* result = realpath(path.c_str(), nullptr);
+  ASSERT_EQ(nullptr, result) << result;
+  ASSERT_EQ(ENOENT, errno);
+  free(result);
+}
+
 TEST(stdlib, qsort) {
   struct s {
     char name[16];