[T106][ZXW-22]7520V3SCV2.01.01.02P42U09_VEC_V0.8_AP_VEC origin source commit

Change-Id: Ic6e05d89ecd62fc34f82b23dcf306c93764aec4b
diff --git a/ap/app/c-ares/src/test/ares-test-ns.cc b/ap/app/c-ares/src/test/ares-test-ns.cc
new file mode 100644
index 0000000..c3c4552
--- /dev/null
+++ b/ap/app/c-ares/src/test/ares-test-ns.cc
@@ -0,0 +1,199 @@
+#include "ares-test.h"
+
+#ifdef HAVE_CONTAINER
+
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <iostream>
+#include <functional>
+#include <string>
+#include <sstream>
+#include <vector>
+
+namespace ares {
+namespace test {
+
+namespace {
+
+struct ContainerInfo {
+  ContainerFilesystem* fs_;
+  std::string hostname_;
+  std::string domainname_;
+  VoidToIntFn fn_;
+};
+
+int EnterContainer(void *data) {
+  ContainerInfo *container = (ContainerInfo*)data;
+
+  if (verbose) {
+    std::cerr << "Running function in container {chroot='"
+              << container->fs_->root() << "', hostname='" << container->hostname_
+              << "', domainname='" << container->domainname_ << "'}"
+              << std::endl;
+  }
+
+  // Ensure we are apparently root before continuing.
+  int count = 10;
+  while (getuid() != 0 && count > 0) {
+    usleep(100000);
+    count--;
+  }
+  if (getuid() != 0) {
+    std::cerr << "Child in user namespace has uid " << getuid() << std::endl;
+    return -1;
+  }
+  if (!container->fs_->mountpt().empty()) {
+    // We want to bind mount this inside the specified directory.
+    std::string innerdir = container->fs_->root() + container->fs_->mountpt();
+    if (verbose) std::cerr << " mount --bind " << container->fs_->mountpt()
+                           << " " << innerdir << std::endl;
+    int rc = mount(container->fs_->mountpt().c_str(), innerdir.c_str(),
+                   "none", MS_BIND, 0);
+    if (rc != 0) {
+      std::cerr << "Warning: failed to bind mount " << container->fs_->mountpt() << " at "
+                << innerdir << ", errno=" << errno << std::endl;
+    }
+  }
+
+  // Move into the specified directory.
+  if (chdir(container->fs_->root().c_str()) != 0) {
+    std::cerr << "Failed to chdir('" << container->fs_->root()
+              << "'), errno=" << errno << std::endl;
+    return -1;
+  }
+  // And make it the new root directory;
+  char buffer[PATH_MAX + 1];
+  if (getcwd(buffer, PATH_MAX) == NULL) {
+    std::cerr << "failed to retrieve cwd, errno=" << errno << std::endl;
+    return -1;
+  }
+  buffer[PATH_MAX] = '\0';
+  if (chroot(buffer) != 0) {
+    std::cerr << "chroot('" << buffer << "') failed, errno=" << errno << std::endl;
+    return -1;
+  }
+
+  // Set host/domainnames if specified
+  if (!container->hostname_.empty()) {
+    if (sethostname(container->hostname_.c_str(),
+                    container->hostname_.size()) != 0) {
+      std::cerr << "Failed to sethostname('" << container->hostname_
+                << "'), errno=" << errno << std::endl;
+      return -1;
+    }
+  }
+  if (!container->domainname_.empty()) {
+    if (setdomainname(container->domainname_.c_str(),
+                      container->domainname_.size()) != 0) {
+      std::cerr << "Failed to setdomainname('" << container->domainname_
+                << "'), errno=" << errno << std::endl;
+      return -1;
+    }
+  }
+
+  return container->fn_();
+}
+
+}  // namespace
+
+// Run a function while:
+//  - chroot()ed into a particular directory
+//  - having a specified hostname/domainname
+
+int RunInContainer(ContainerFilesystem* fs, const std::string& hostname,
+                   const std::string& domainname, VoidToIntFn fn) {
+  const int stack_size = 1024 * 1024;
+  std::vector<byte> stack(stack_size, 0);
+  ContainerInfo container = {fs, hostname, domainname, fn};
+
+  // Start a child process in a new user and UTS namespace
+  pid_t child = clone(EnterContainer, stack.data() + stack_size,
+                      CLONE_VM|CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWUTS|SIGCHLD,
+                      (void *)&container);
+  if (child < 0) {
+    std::cerr << "Failed to clone(), errno=" << errno << std::endl;
+    return -1;
+  }
+
+  // Build the UID map that makes us look like root inside the namespace.
+  std::stringstream mapfiless;
+  mapfiless << "/proc/" << child << "/uid_map";
+  std::string mapfile = mapfiless.str();
+  int fd = open(mapfile.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 0644);
+  if (fd < 0) {
+    std::cerr << "Failed to create '" << mapfile << "'" << std::endl;
+    return -1;
+  }
+  std::stringstream contentss;
+  contentss << "0 " << getuid() << " 1" << std::endl;
+  std::string content = contentss.str();
+  int rc = write(fd, content.c_str(), content.size());
+  if (rc != (int)content.size()) {
+    std::cerr << "Failed to write uid map to '" << mapfile << "'" << std::endl;
+  }
+  close(fd);
+
+  // Wait for the child process and retrieve its status.
+  int status;
+  waitpid(child, &status, 0);
+  if (rc <= 0) {
+    std::cerr << "Failed to waitpid(" << child << ")" << std::endl;
+    return -1;
+  }
+  if (!WIFEXITED(status)) {
+    std::cerr << "Child " << child << " did not exit normally" << std::endl;
+    return -1;
+  }
+  return status;
+}
+
+ContainerFilesystem::ContainerFilesystem(NameContentList files, const std::string& mountpt) {
+  rootdir_ = TempNam(nullptr, "ares-chroot");
+  mkdir(rootdir_.c_str(), 0755);
+  dirs_.push_front(rootdir_);
+  for (const auto& nc : files) {
+    std::string fullpath = rootdir_ + nc.first;
+    int idx = fullpath.rfind('/');
+    std::string dir = fullpath.substr(0, idx);
+    EnsureDirExists(dir);
+    files_.push_back(std::unique_ptr<TransientFile>(
+        new TransientFile(fullpath, nc.second)));
+  }
+  if (!mountpt.empty()) {
+    char buffer[PATH_MAX + 1];
+    if (realpath(mountpt.c_str(), buffer)) {
+      mountpt_ = buffer;
+      std::string fullpath = rootdir_ + mountpt_;
+      EnsureDirExists(fullpath);
+    }
+  }
+}
+
+ContainerFilesystem::~ContainerFilesystem() {
+  files_.clear();
+  for (const std::string& dir : dirs_) {
+    rmdir(dir.c_str());
+  }
+}
+
+void ContainerFilesystem::EnsureDirExists(const std::string& dir) {
+  if (std::find(dirs_.begin(), dirs_.end(), dir) != dirs_.end()) {
+    return;
+  }
+  size_t idx = dir.rfind('/');
+  if (idx != std::string::npos) {
+    std::string prevdir = dir.substr(0, idx);
+    EnsureDirExists(prevdir);
+  }
+  // Ensure this directory is in the list before its ancestors.
+  mkdir(dir.c_str(), 0755);
+  dirs_.push_front(dir);
+}
+
+}  // namespace test
+}  // namespace ares
+
+#endif