Make Temporary{Dir,File} available for all unittest targets.

These scoped classes are generally useful for unit tests, so make them
available for all unit tests.

In particular, we're going to use them for a test to validate an
upcoming fix that will prevent Minijail from following symlinks in
bind mount sources and destinations.

Bug: 219093918
Test: New unit test, existing tests pass.
Change-Id: I5667d0030af083aea94803555fcac6635c1638cc
diff --git a/libminijail_unittest.cc b/libminijail_unittest.cc
index c20e32b..35d5ca5 100644
--- a/libminijail_unittest.cc
+++ b/libminijail_unittest.cc
@@ -26,6 +26,7 @@
 #include "libminijail-private.h"
 #include "libminijail.h"
 #include "scoped_minijail.h"
+#include "unittest_util.h"
 #include "util.h"
 
 namespace {
@@ -1006,6 +1007,35 @@
   minijail_destroy(j);
 }
 
+// Test that bind mounting with a symlink works (but we're about to make this
+// fail).
+TEST(Test, test_bind_mount_symlink) {
+  TemporaryDir dir;
+  ASSERT_TRUE(dir.is_valid());
+
+  // minijail_bind() expects absolute paths, but TemporaryDir::path can return
+  // relative paths on Linux.
+  std::string path = dir.path;
+  if (!is_android()) {
+    std::string cwd(getcwd(NULL, 0));
+    path = cwd + "/" + path;
+  }
+
+  std::string path_src = path + "/src";
+  std::string path_dest = path + "/dest";
+  std::string path_sym = path + "/symlink";
+
+  EXPECT_EQ(mkdir(path_src.c_str(), 0700), 0);
+  EXPECT_EQ(mkdir(path_dest.c_str(), 0700), 0);
+  EXPECT_EQ(symlink(path_src.c_str(), path_sym.c_str()), 0);
+
+  ScopedMinijail j(minijail_new());
+  int bind_res = minijail_bind(j.get(), path_sym.c_str(), path_dest.c_str(),
+                               0 /*writable*/);
+  EXPECT_EQ(bind_res, 0);
+  EXPECT_EQ(unlink(path_sym.c_str()), 0);
+}
+
 namespace {
 
 // Tests that require userns access.
diff --git a/system_unittest.cc b/system_unittest.cc
index 92b7ab9..19b9099 100644
--- a/system_unittest.cc
+++ b/system_unittest.cc
@@ -20,6 +20,7 @@
 #include <string>
 
 #include "system.h"
+#include "unittest_util.h"
 
 namespace {
 
@@ -35,91 +36,6 @@
 // A random character device that should exist.
 constexpr const char kValidCharDev[] = "/dev/null";
 
-constexpr bool is_android() {
-#if defined(__ANDROID__)
-  return true;
-#else
-  return false;
-#endif
-}
-
-// Returns a template path that can be used as an argument to mkstemp / mkdtemp.
-constexpr const char* temp_path_pattern() {
-  if (is_android())
-    return "/data/local/tmp/minijail.tests.XXXXXX";
-  else
-    return "minijail.tests.XXXXXX";
-}
-
-// Recursively deletes the subtree rooted at |path|.
-bool rmdir_recursive(const std::string& path) {
-  auto callback = [](const char* child, const struct stat*, int file_type,
-                     struct FTW*) -> int {
-    if (file_type == FTW_DP) {
-      if (rmdir(child) == -1) {
-        fprintf(stderr, "rmdir(%s): %s", child, strerror(errno));
-        return -1;
-      }
-    } else if (file_type == FTW_F) {
-      if (unlink(child) == -1) {
-        fprintf(stderr, "unlink(%s): %s", child, strerror(errno));
-        return -1;
-      }
-    }
-    return 0;
-  };
-
-  return nftw(path.c_str(), callback, 128, FTW_DEPTH) == 0;
-}
-
-// Creates a temporary directory that will be cleaned up upon leaving scope.
-class TemporaryDir {
- public:
-  TemporaryDir() : path(temp_path_pattern()) {
-    if (mkdtemp(const_cast<char*>(path.c_str())) == nullptr)
-      path.clear();
-  }
-  ~TemporaryDir() {
-    if (!is_valid())
-      return;
-    rmdir_recursive(path.c_str());
-  }
-
-  bool is_valid() const { return !path.empty(); }
-
-  std::string path;
-
- private:
-  TemporaryDir(const TemporaryDir&) = delete;
-  TemporaryDir& operator=(const TemporaryDir&) = delete;
-};
-
-// Creates a named temporary file that will be cleaned up upon leaving scope.
-class TemporaryFile {
- public:
-  TemporaryFile() : path(temp_path_pattern()) {
-    int fd = mkstemp(const_cast<char*>(path.c_str()));
-    if (fd == -1) {
-      path.clear();
-      return;
-    }
-    close(fd);
-  }
-  ~TemporaryFile() {
-    if (!is_valid())
-      return;
-    unlink(path.c_str());
-  }
-
-  bool is_valid() const { return !path.empty(); }
-
-  std::string path;
-
- private:
-  TemporaryFile(const TemporaryFile&) = delete;
-  TemporaryFile& operator=(const TemporaryFile&) = delete;
-};
-
 }  // namespace
 
 TEST(secure_noroot_set_and_locked, zero_mask) {
diff --git a/unittest_util.h b/unittest_util.h
new file mode 100644
index 0000000..54993d7
--- /dev/null
+++ b/unittest_util.h
@@ -0,0 +1,103 @@
+/* unittest_util.h
+ * Copyright 2022 The ChromiumOS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Utility functions for unit tests.
+ */
+
+#include <ftw.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "util.h"
+
+namespace {
+
+constexpr bool is_android_constexpr() {
+#if defined(__ANDROID__)
+  return true;
+#else
+  return false;
+#endif
+}
+
+// Returns a template path that can be used as an argument to mkstemp / mkdtemp.
+constexpr const char* temp_path_pattern() {
+  if (is_android_constexpr())
+    return "/data/local/tmp/minijail.tests.XXXXXX";
+  else
+    return "minijail.tests.XXXXXX";
+}
+
+// Recursively deletes the subtree rooted at |path|.
+bool rmdir_recursive(const std::string& path) {
+  auto callback = [](const char* child, const struct stat*, int file_type,
+                     struct FTW*) -> int {
+    if (file_type == FTW_DP) {
+      if (rmdir(child) == -1) {
+        fprintf(stderr, "rmdir(%s): %s\n", child, strerror(errno));
+        return -1;
+      }
+    } else if (file_type == FTW_F) {
+      if (unlink(child) == -1) {
+        fprintf(stderr, "unlink(%s): %s\n", child, strerror(errno));
+        return -1;
+      }
+    }
+    return 0;
+  };
+
+  return nftw(path.c_str(), callback, 128, FTW_DEPTH) == 0;
+}
+
+}  // namespace
+
+// Creates a temporary directory that will be cleaned up upon leaving scope.
+class TemporaryDir {
+ public:
+  TemporaryDir() : path(temp_path_pattern()) {
+    if (mkdtemp(const_cast<char*>(path.c_str())) == nullptr)
+      path.clear();
+  }
+  ~TemporaryDir() {
+    if (!is_valid())
+      return;
+    rmdir_recursive(path.c_str());
+  }
+
+  bool is_valid() const { return !path.empty(); }
+
+  std::string path;
+
+ private:
+  TemporaryDir(const TemporaryDir&) = delete;
+  TemporaryDir& operator=(const TemporaryDir&) = delete;
+};
+
+// Creates a named temporary file that will be cleaned up upon leaving scope.
+class TemporaryFile {
+ public:
+  TemporaryFile() : path(temp_path_pattern()) {
+    int fd = mkstemp(const_cast<char*>(path.c_str()));
+    if (fd == -1) {
+      path.clear();
+      return;
+    }
+    close(fd);
+  }
+  ~TemporaryFile() {
+    if (!is_valid())
+      return;
+    unlink(path.c_str());
+  }
+
+  bool is_valid() const { return !path.empty(); }
+
+  std::string path;
+
+ private:
+  TemporaryFile(const TemporaryFile&) = delete;
+  TemporaryFile& operator=(const TemporaryFile&) = delete;
+};