CLOEXEC support in fdopen, freopen, and mkostemp/mkostemps.

Change-Id: I74ea88e0d4973d6ab3c57da7d8bb643c31592b14
diff --git a/tests/TemporaryFile.h b/tests/TemporaryFile.h
index c4ee2d5..5c2fe4f 100644
--- a/tests/TemporaryFile.h
+++ b/tests/TemporaryFile.h
@@ -17,24 +17,26 @@
 #include <fcntl.h>
 #include <unistd.h>
 
-template<int (*mk_fn)(char*)>
+#include "private/bionic_macros.h"
+
+template <typename T = int (*)(char*)>
 class GenericTemporaryFile {
  public:
-  GenericTemporaryFile(const char* dirpath = NULL) {
-    if (dirpath != NULL) {
-      init(dirpath);
-    } else {
-      // Since we might be running on the host or the target, and if we're
-      // running on the host we might be running under bionic or glibc,
-      // let's just try both possible temporary directories and take the
-      // first one that works.
-      init("/data/local/tmp");
-      if (fd == -1) {
-        init("/tmp");
-      }
+  GenericTemporaryFile(T mk_fn = mkstemp) : mk_fn(mk_fn) {
+    // Since we might be running on the host or the target, and if we're
+    // running on the host we might be running under bionic or glibc,
+    // let's just try both possible temporary directories and take the
+    // first one that works.
+    init("/data/local/tmp");
+    if (fd == -1) {
+      init("/tmp");
     }
   }
 
+  GenericTemporaryFile(const char* dirpath, T mk_fn = mkstemp) : mk_fn(mk_fn) {
+    init(dirpath);
+  }
+
   ~GenericTemporaryFile() {
     close(fd);
     unlink(filename);
@@ -49,13 +51,17 @@
   char filename[1024];
 
  private:
+  T mk_fn;
+
   void init(const char* tmp_dir) {
     snprintf(filename, sizeof(filename), "%s/TemporaryFile-XXXXXX", tmp_dir);
     fd = mk_fn(filename);
   }
+
+  DISALLOW_COPY_AND_ASSIGN(GenericTemporaryFile);
 };
 
-typedef GenericTemporaryFile<mkstemp> TemporaryFile;
+typedef GenericTemporaryFile<> TemporaryFile;
 
 class TemporaryDir {
  public:
@@ -77,4 +83,5 @@
     return (mkdtemp(dirname) != NULL);
   }
 
+  DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
 };
diff --git a/tests/ftw_test.cpp b/tests/ftw_test.cpp
index 6d3a308..7ffbfe0 100644
--- a/tests/ftw_test.cpp
+++ b/tests/ftw_test.cpp
@@ -68,7 +68,7 @@
 
 TEST(ftw, ftw64) {
   TemporaryDir td;
-  GenericTemporaryFile<mkstemp64> tf(td.dirname);
+  TemporaryFile tf(td.dirname, mkstemp64);
   ftw64(td.dirname, check_ftw64, 1);
 }
 
@@ -80,6 +80,6 @@
 
 TEST(ftw, nftw64) {
   TemporaryDir td;
-  GenericTemporaryFile<mkstemp64> tf(td.dirname);
+  TemporaryFile tf(td.dirname, mkstemp64);
   nftw64(td.dirname, check_nftw64, 1, 0);
 }
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index c01ab68..6a653b4 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -756,3 +756,43 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
+
+TEST(stdio, fdopen_CLOEXEC) {
+  int fd = open("/proc/version", O_RDONLY);
+  ASSERT_TRUE(fd != -1);
+
+  // This fd doesn't have O_CLOEXEC...
+  int flags = fcntl(fd, F_GETFD);
+  ASSERT_TRUE(flags != -1);
+  ASSERT_EQ(0, flags & FD_CLOEXEC);
+
+  FILE* fp = fdopen(fd, "re");
+  ASSERT_TRUE(fp != NULL);
+
+  // ...but the new one does.
+  flags = fcntl(fileno(fp), F_GETFD);
+  ASSERT_TRUE(flags != -1);
+  ASSERT_EQ(FD_CLOEXEC, flags & FD_CLOEXEC);
+
+  fclose(fp);
+  close(fd);
+}
+
+TEST(stdio, freopen_CLOEXEC) {
+  FILE* fp = fopen("/proc/version", "r");
+  ASSERT_TRUE(fp != NULL);
+
+  // This FILE* doesn't have O_CLOEXEC...
+  int flags = fcntl(fileno(fp), F_GETFD);
+  ASSERT_TRUE(flags != -1);
+  ASSERT_EQ(0, flags & FD_CLOEXEC);
+
+  fp = freopen("/proc/version", "re", fp);
+
+  // ...but the new one does.
+  flags = fcntl(fileno(fp), F_GETFD);
+  ASSERT_TRUE(flags != -1);
+  ASSERT_EQ(FD_CLOEXEC, flags & FD_CLOEXEC);
+
+  fclose(fp);
+}
diff --git a/tests/stdlib_test.cpp b/tests/stdlib_test.cpp
index 667ccd6..e814ef7 100644
--- a/tests/stdlib_test.cpp
+++ b/tests/stdlib_test.cpp
@@ -162,19 +162,33 @@
   ASSERT_EXIT(TestBug57421_main(), ::testing::ExitedWithCode(0), "");
 }
 
+TEST(stdlib, mkostemp64) {
+  TemporaryFile tf([](char* path) { return mkostemp64(path, O_CLOEXEC); });
+  int flags = fcntl(tf.fd, F_GETFD);
+  ASSERT_TRUE(flags != -1);
+  ASSERT_EQ(FD_CLOEXEC, flags & FD_CLOEXEC);
+}
+
+TEST(stdlib, mkostemp) {
+  TemporaryFile tf([](char* path) { return mkostemp(path, O_CLOEXEC); });
+  int flags = fcntl(tf.fd, F_GETFD);
+  ASSERT_TRUE(flags != -1);
+  ASSERT_EQ(FD_CLOEXEC, flags & FD_CLOEXEC);
+}
+
+TEST(stdlib, mkstemp64) {
+  TemporaryFile tf(mkstemp64);
+  struct stat64 sb;
+  ASSERT_EQ(0, fstat64(tf.fd, &sb));
+  ASSERT_EQ(O_LARGEFILE, fcntl(tf.fd, F_GETFL) & O_LARGEFILE);
+}
+
 TEST(stdlib, mkstemp) {
   TemporaryFile tf;
   struct stat sb;
   ASSERT_EQ(0, fstat(tf.fd, &sb));
 }
 
-TEST(stdlib, mkstemp64) {
-  GenericTemporaryFile<mkstemp64> tf;
-  struct stat64 sb;
-  ASSERT_EQ(0, fstat64(tf.fd, &sb));
-  ASSERT_EQ(O_LARGEFILE, fcntl(tf.fd, F_GETFL) & O_LARGEFILE);
-}
-
 TEST(stdlib, system) {
   int status;