diff options
Diffstat (limited to 'test/util')
57 files changed, 0 insertions, 7342 deletions
diff --git a/test/util/BUILD b/test/util/BUILD deleted file mode 100644 index 8985b54af..000000000 --- a/test/util/BUILD +++ /dev/null @@ -1,403 +0,0 @@ -load("//tools:defs.bzl", "cc_library", "cc_test", "coreutil", "gbenchmark", "gtest", "select_system") - -package( - default_visibility = ["//:sandbox"], - licenses = ["notice"], -) - -cc_library( - name = "capability_util", - testonly = 1, - srcs = ["capability_util.cc"], - hdrs = ["capability_util.h"], - deps = [ - ":cleanup", - ":memory_util", - ":posix_error", - ":save_util", - ":test_util", - "@com_google_absl//absl/strings", - ], -) - -cc_library( - name = "eventfd_util", - testonly = 1, - hdrs = ["eventfd_util.h"], - deps = [ - ":file_descriptor", - ":posix_error", - ":save_util", - ], -) - -cc_library( - name = "file_descriptor", - testonly = 1, - hdrs = ["file_descriptor.h"], - deps = [ - ":logging", - ":posix_error", - ":save_util", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - gtest, - ], -) - -cc_library( - name = "fuse_util", - testonly = 1, - srcs = ["fuse_util.cc"], - hdrs = ["fuse_util.h"], -) - -cc_library( - name = "proc_util", - testonly = 1, - srcs = ["proc_util.cc"], - hdrs = ["proc_util.h"], - deps = [ - ":fs_util", - ":posix_error", - ":test_util", - "@com_google_absl//absl/strings", - gtest, - ], -) - -cc_test( - name = "proc_util_test", - size = "small", - srcs = ["proc_util_test.cc"], - deps = [ - ":proc_util", - ":test_main", - ":test_util", - gtest, - ], -) - -cc_library( - name = "cleanup", - testonly = 1, - hdrs = ["cleanup.h"], -) - -cc_library( - name = "fs_util", - testonly = 1, - srcs = ["fs_util.cc"], - hdrs = ["fs_util.h"], - deps = [ - ":cleanup", - ":file_descriptor", - ":posix_error", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - gtest, - ], -) - -cc_test( - name = "fs_util_test", - size = "small", - srcs = ["fs_util_test.cc"], - deps = [ - ":fs_util", - ":posix_error", - ":temp_path", - ":test_main", - ":test_util", - gtest, - ], -) - -cc_library( - name = "logging", - testonly = 1, - srcs = ["logging.cc"], - hdrs = ["logging.h"], -) - -cc_library( - name = "memory_util", - testonly = 1, - hdrs = ["memory_util.h"], - deps = [ - ":logging", - ":posix_error", - ":save_util", - ":test_util", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - ], -) - -cc_library( - name = "mount_util", - testonly = 1, - srcs = ["mount_util.cc"], - hdrs = ["mount_util.h"], - deps = [ - ":cleanup", - ":posix_error", - ":test_util", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/strings", - gtest, - ], -) - -cc_test( - name = "mount_util_test", - size = "small", - srcs = ["mount_util_test.cc"], - deps = [ - ":mount_util", - ":test_main", - ":test_util", - gtest, - ], -) - -cc_library( - name = "save_util", - testonly = 1, - srcs = [ - "save_util.cc", - "save_util_linux.cc", - "save_util_other.cc", - ], - hdrs = ["save_util.h"], - defines = select_system(), - deps = [ - ":logging", - "@com_google_absl//absl/types:optional", - ], -) - -cc_library( - name = "multiprocess_util", - testonly = 1, - srcs = ["multiprocess_util.cc"], - hdrs = ["multiprocess_util.h"], - deps = [ - ":cleanup", - ":file_descriptor", - ":posix_error", - ":save_util", - ":test_util", - gtest, - "@com_google_absl//absl/strings", - ], -) - -cc_library( - name = "platform_util", - testonly = 1, - srcs = ["platform_util.cc"], - hdrs = ["platform_util.h"], - deps = [":test_util"], -) - -cc_library( - name = "posix_error", - testonly = 1, - srcs = ["posix_error.cc"], - hdrs = ["posix_error.h"], - deps = [ - ":logging", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/types:variant", - gtest, - ], -) - -cc_test( - name = "posix_error_test", - size = "small", - srcs = ["posix_error_test.cc"], - deps = [ - ":posix_error", - ":test_main", - gtest, - ], -) - -cc_library( - name = "pty_util", - testonly = 1, - srcs = ["pty_util.cc"], - hdrs = ["pty_util.h"], - deps = [ - ":file_descriptor", - ":posix_error", - ], -) - -cc_library( - name = "signal_util", - testonly = 1, - srcs = ["signal_util.cc"], - hdrs = ["signal_util.h"], - deps = [ - ":cleanup", - ":posix_error", - ":test_util", - gtest, - ], -) - -cc_library( - name = "temp_path", - testonly = 1, - srcs = ["temp_path.cc"], - hdrs = ["temp_path.h"], - deps = [ - ":fs_util", - ":posix_error", - ":test_util", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - gtest, - ], -) - -cc_library( - name = "test_util", - testonly = 1, - srcs = [ - "test_util.cc", - "test_util_impl.cc", - "test_util_runfiles.cc", - ], - hdrs = ["test_util.h"], - defines = select_system(), - deps = coreutil() + [ - ":fs_util", - ":logging", - ":posix_error", - ":save_util", - "@bazel_tools//tools/cpp/runfiles", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/flags:flag", - "@com_google_absl//absl/flags:parse", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - "@com_google_absl//absl/time", - gtest, - gbenchmark, - ], -) - -cc_library( - name = "thread_util", - testonly = 1, - hdrs = ["thread_util.h"], - deps = [":logging"], -) - -cc_library( - name = "time_util", - testonly = 1, - srcs = ["time_util.cc"], - hdrs = ["time_util.h"], - deps = [ - "@com_google_absl//absl/time", - ], -) - -cc_library( - name = "timer_util", - testonly = 1, - srcs = ["timer_util.cc"], - hdrs = ["timer_util.h"], - deps = [ - ":cleanup", - ":logging", - ":posix_error", - ":test_util", - "@com_google_absl//absl/time", - gtest, - ], -) - -cc_test( - name = "test_util_test", - size = "small", - srcs = ["test_util_test.cc"], - deps = [ - ":test_main", - ":test_util", - gtest, - ], -) - -cc_library( - name = "test_main", - testonly = 1, - srcs = ["test_main.cc"], - deps = [":test_util"], -) - -cc_library( - name = "epoll_util", - testonly = 1, - srcs = ["epoll_util.cc"], - hdrs = ["epoll_util.h"], - deps = [ - ":file_descriptor", - ":posix_error", - ":save_util", - gtest, - ], -) - -cc_library( - name = "rlimit_util", - testonly = 1, - srcs = ["rlimit_util.cc"], - hdrs = ["rlimit_util.h"], - deps = [ - ":cleanup", - ":logging", - ":posix_error", - ":test_util", - ], -) - -cc_library( - name = "uid_util", - testonly = 1, - srcs = ["uid_util.cc"], - hdrs = ["uid_util.h"], - deps = [ - ":posix_error", - ":save_util", - ], -) - -cc_library( - name = "temp_umask", - testonly = 1, - hdrs = ["temp_umask.h"], -) - -cc_library( - name = "cgroup_util", - testonly = 1, - srcs = ["cgroup_util.cc"], - hdrs = ["cgroup_util.h"], - deps = [ - ":cleanup", - ":fs_util", - ":mount_util", - ":posix_error", - ":temp_path", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/container:flat_hash_set", - "@com_google_absl//absl/strings", - ], -) diff --git a/test/util/capability_util.cc b/test/util/capability_util.cc deleted file mode 100644 index a1b994c45..000000000 --- a/test/util/capability_util.cc +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/capability_util.h" - -#include <linux/capability.h> -#include <sched.h> -#include <sys/mman.h> -#include <sys/wait.h> - -#include <iostream> - -#include "absl/strings/str_cat.h" -#include "test/util/memory_util.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr<bool> CanCreateUserNamespace() { - // The most reliable way to determine if userns creation is possible is by - // trying to create one; see below. - ASSIGN_OR_RETURN_ERRNO( - auto child_stack, - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - int const child_pid = clone( - +[](void*) { return 0; }, - reinterpret_cast<void*>(child_stack.addr() + kPageSize), - CLONE_NEWUSER | SIGCHLD, /* arg = */ nullptr); - if (child_pid > 0) { - int status; - int const ret = waitpid(child_pid, &status, /* options = */ 0); - MaybeSave(); - if (ret < 0) { - return PosixError(errno, "waitpid"); - } - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - return PosixError( - ESRCH, absl::StrCat("child process exited with status ", status)); - } - return true; - } else if (errno == EPERM) { - // Per clone(2), EPERM can be returned if: - // - // - "CLONE_NEWUSER was specified in flags, but either the effective user ID - // or the effective group ID of the caller does not have a mapping in the - // parent namespace (see user_namespaces(7))." - // - // - "(since Linux 3.9) CLONE_NEWUSER was specified in flags and the caller - // is in a chroot environment (i.e., the caller's root directory does - // not match the root directory of the mount namespace in which it - // resides)." - std::cerr << "clone(CLONE_NEWUSER) failed with EPERM" << std::endl; - return false; - } else if (errno == EUSERS) { - // "(since Linux 3.11) CLONE_NEWUSER was specified in flags, and the call - // would cause the limit on the number of nested user namespaces to be - // exceeded. See user_namespaces(7)." - std::cerr << "clone(CLONE_NEWUSER) failed with EUSERS" << std::endl; - return false; - } else { - // Unexpected error code; indicate an actual error. - return PosixError(errno, "clone(CLONE_NEWUSER)"); - } -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/capability_util.h b/test/util/capability_util.h deleted file mode 100644 index f2c370125..000000000 --- a/test/util/capability_util.h +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Utilities for testing capabilities. - -#ifndef GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ -#define GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ - -#include <errno.h> -#include <linux/capability.h> -#include <sys/syscall.h> -#include <unistd.h> - -#include "test/util/cleanup.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/test_util.h" - -#ifndef _LINUX_CAPABILITY_VERSION_3 -#error Expecting _LINUX_CAPABILITY_VERSION_3 support -#endif - -namespace gvisor { -namespace testing { - -// HaveCapability returns true if the process has the specified EFFECTIVE -// capability. -inline PosixErrorOr<bool> HaveCapability(int cap) { - if (!cap_valid(cap)) { - return PosixError(EINVAL, "Invalid capability"); - } - - struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0}; - struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {}; - RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps)); - MaybeSave(); - - return (caps[CAP_TO_INDEX(cap)].effective & CAP_TO_MASK(cap)) != 0; -} - -// SetCapability sets the specified EFFECTIVE capability. -inline PosixError SetCapability(int cap, bool set) { - if (!cap_valid(cap)) { - return PosixError(EINVAL, "Invalid capability"); - } - - struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0}; - struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {}; - RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps)); - MaybeSave(); - - if (set) { - caps[CAP_TO_INDEX(cap)].effective |= CAP_TO_MASK(cap); - } else { - caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap); - } - header = {_LINUX_CAPABILITY_VERSION_3, 0}; - RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps)); - MaybeSave(); - - return NoError(); -} - -// DropPermittedCapability drops the specified PERMITTED. The EFFECTIVE -// capabilities must be a subset of PERMITTED, so those are dropped as well. -inline PosixError DropPermittedCapability(int cap) { - if (!cap_valid(cap)) { - return PosixError(EINVAL, "Invalid capability"); - } - - struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0}; - struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {}; - RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps)); - MaybeSave(); - - caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap); - caps[CAP_TO_INDEX(cap)].permitted &= ~CAP_TO_MASK(cap); - - header = {_LINUX_CAPABILITY_VERSION_3, 0}; - RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps)); - MaybeSave(); - - return NoError(); -} - -PosixErrorOr<bool> CanCreateUserNamespace(); - -class AutoCapability { - public: - AutoCapability(int cap, bool set) : cap_(cap), set_(set) { - const bool has = EXPECT_NO_ERRNO_AND_VALUE(HaveCapability(cap)); - if (set != has) { - EXPECT_NO_ERRNO(SetCapability(cap_, set_)); - applied_ = true; - } - } - - ~AutoCapability() { - if (applied_) { - EXPECT_NO_ERRNO(SetCapability(cap_, !set_)); - } - } - - private: - int cap_; - bool set_; - bool applied_ = false; -}; - -} // namespace testing -} // namespace gvisor -#endif // GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ diff --git a/test/util/cgroup_util.cc b/test/util/cgroup_util.cc deleted file mode 100644 index 977993f41..000000000 --- a/test/util/cgroup_util.cc +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright 2021 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/cgroup_util.h" - -#include <sys/syscall.h> -#include <unistd.h> - -#include "absl/strings/str_cat.h" -#include "absl/strings/str_split.h" -#include "test/util/fs_util.h" -#include "test/util/mount_util.h" - -namespace gvisor { -namespace testing { - -Cgroup::Cgroup(std::string_view path) : cgroup_path_(path) { - id_ = ++Cgroup::next_id_; - std::cerr << absl::StreamFormat("[cg#%d] <= %s", id_, cgroup_path_) - << std::endl; -} - -PosixErrorOr<std::string> Cgroup::ReadControlFile( - absl::string_view name) const { - std::string buf; - RETURN_IF_ERRNO(GetContents(Relpath(name), &buf)); - - const std::string alias_path = absl::StrFormat("[cg#%d]/%s", id_, name); - std::cerr << absl::StreamFormat("<contents of %s>", alias_path) << std::endl; - std::cerr << buf; - std::cerr << absl::StreamFormat("<end of %s>", alias_path) << std::endl; - - return buf; -} - -PosixErrorOr<int64_t> Cgroup::ReadIntegerControlFile( - absl::string_view name) const { - ASSIGN_OR_RETURN_ERRNO(const std::string buf, ReadControlFile(name)); - ASSIGN_OR_RETURN_ERRNO(const int64_t val, Atoi<int64_t>(buf)); - return val; -} - -PosixError Cgroup::WriteControlFile(absl::string_view name, - const std::string& value) const { - ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, Open(Relpath(name), O_WRONLY)); - RETURN_ERROR_IF_SYSCALL_FAIL(WriteFd(fd.get(), value.c_str(), value.size())); - return NoError(); -} - -PosixError Cgroup::WriteIntegerControlFile(absl::string_view name, - int64_t value) const { - return WriteControlFile(name, absl::StrCat(value)); -} - -PosixErrorOr<absl::flat_hash_set<pid_t>> Cgroup::Procs() const { - ASSIGN_OR_RETURN_ERRNO(std::string buf, ReadControlFile("cgroup.procs")); - return ParsePIDList(buf); -} - -PosixErrorOr<absl::flat_hash_set<pid_t>> Cgroup::Tasks() const { - ASSIGN_OR_RETURN_ERRNO(std::string buf, ReadControlFile("tasks")); - return ParsePIDList(buf); -} - -PosixError Cgroup::ContainsCallingProcess() const { - ASSIGN_OR_RETURN_ERRNO(const absl::flat_hash_set<pid_t> procs, Procs()); - ASSIGN_OR_RETURN_ERRNO(const absl::flat_hash_set<pid_t> tasks, Tasks()); - const pid_t pid = getpid(); - const pid_t tid = syscall(SYS_gettid); - if (!procs.contains(pid)) { - return PosixError( - ENOENT, absl::StrFormat("Cgroup doesn't contain process %d", pid)); - } - if (!tasks.contains(tid)) { - return PosixError(ENOENT, - absl::StrFormat("Cgroup doesn't contain task %d", tid)); - } - return NoError(); -} - -PosixErrorOr<absl::flat_hash_set<pid_t>> Cgroup::ParsePIDList( - absl::string_view data) const { - absl::flat_hash_set<pid_t> res; - std::vector<absl::string_view> lines = absl::StrSplit(data, '\n'); - for (const std::string_view& line : lines) { - if (line.empty()) { - continue; - } - ASSIGN_OR_RETURN_ERRNO(const int32_t pid, Atoi<int32_t>(line)); - res.insert(static_cast<pid_t>(pid)); - } - return res; -} - -int64_t Cgroup::next_id_ = 0; - -PosixErrorOr<Cgroup> Mounter::MountCgroupfs(std::string mopts) { - ASSIGN_OR_RETURN_ERRNO(TempPath mountpoint, - TempPath::CreateDirIn(root_.path())); - ASSIGN_OR_RETURN_ERRNO( - Cleanup mount, Mount("none", mountpoint.path(), "cgroup", 0, mopts, 0)); - const std::string mountpath = mountpoint.path(); - std::cerr << absl::StreamFormat( - "Mount(\"none\", \"%s\", \"cgroup\", 0, \"%s\", 0) => OK", - mountpath, mopts) - << std::endl; - Cgroup cg = Cgroup(mountpath); - mountpoints_[cg.id()] = std::move(mountpoint); - mounts_[cg.id()] = std::move(mount); - return cg; -} - -PosixError Mounter::Unmount(const Cgroup& c) { - auto mount = mounts_.find(c.id()); - auto mountpoint = mountpoints_.find(c.id()); - - if (mount == mounts_.end() || mountpoint == mountpoints_.end()) { - return PosixError( - ESRCH, absl::StrFormat("No mount found for cgroupfs containing cg#%d", - c.id())); - } - - std::cerr << absl::StreamFormat("Unmount([cg#%d])", c.id()) << std::endl; - - // Simply delete the entries, their destructors will unmount and delete the - // mountpoint. Note the order is important to avoid errors: mount then - // mountpoint. - mounts_.erase(mount); - mountpoints_.erase(mountpoint); - - return NoError(); -} - -void Mounter::release(const Cgroup& c) { - auto mp = mountpoints_.find(c.id()); - if (mp != mountpoints_.end()) { - mp->second.release(); - mountpoints_.erase(mp); - } - - auto m = mounts_.find(c.id()); - if (m != mounts_.end()) { - m->second.Release(); - mounts_.erase(m); - } -} - -constexpr char kProcCgroupsHeader[] = - "#subsys_name\thierarchy\tnum_cgroups\tenabled"; - -PosixErrorOr<absl::flat_hash_map<std::string, CgroupsEntry>> -ProcCgroupsEntries() { - std::string content; - RETURN_IF_ERRNO(GetContents("/proc/cgroups", &content)); - - bool found_header = false; - absl::flat_hash_map<std::string, CgroupsEntry> entries; - std::vector<std::string> lines = absl::StrSplit(content, '\n'); - std::cerr << "<contents of /proc/cgroups>" << std::endl; - for (const std::string& line : lines) { - std::cerr << line << std::endl; - - if (!found_header) { - EXPECT_EQ(line, kProcCgroupsHeader); - found_header = true; - continue; - } - if (line.empty()) { - continue; - } - - // Parse a single entry from /proc/cgroups. - // - // Example entries, fields are tab separated in the real file: - // - // #subsys_name hierarchy num_cgroups enabled - // cpuset 12 35 1 - // cpu 3 222 1 - // ^ ^ ^ ^ - // 0 1 2 3 - - CgroupsEntry entry; - std::vector<std::string> fields = - StrSplit(line, absl::ByAnyChar(": \t"), absl::SkipEmpty()); - - entry.subsys_name = fields[0]; - ASSIGN_OR_RETURN_ERRNO(entry.hierarchy, Atoi<uint32_t>(fields[1])); - ASSIGN_OR_RETURN_ERRNO(entry.num_cgroups, Atoi<uint64_t>(fields[2])); - ASSIGN_OR_RETURN_ERRNO(const int enabled, Atoi<int>(fields[3])); - entry.enabled = enabled != 0; - - entries[entry.subsys_name] = entry; - } - std::cerr << "<end of /proc/cgroups>" << std::endl; - - return entries; -} - -PosixErrorOr<absl::flat_hash_map<std::string, PIDCgroupEntry>> -ProcPIDCgroupEntries(pid_t pid) { - const std::string path = absl::StrFormat("/proc/%d/cgroup", pid); - std::string content; - RETURN_IF_ERRNO(GetContents(path, &content)); - - absl::flat_hash_map<std::string, PIDCgroupEntry> entries; - std::vector<std::string> lines = absl::StrSplit(content, '\n'); - - std::cerr << absl::StreamFormat("<contents of %s>", path) << std::endl; - for (const std::string& line : lines) { - std::cerr << line << std::endl; - - if (line.empty()) { - continue; - } - - // Parse a single entry from /proc/<pid>/cgroup. - // - // Example entries: - // - // 2:cpu:/path/to/cgroup - // 1:memory:/ - - PIDCgroupEntry entry; - std::vector<std::string> fields = - absl::StrSplit(line, absl::ByChar(':'), absl::SkipEmpty()); - - ASSIGN_OR_RETURN_ERRNO(entry.hierarchy, Atoi<uint32_t>(fields[0])); - entry.controllers = fields[1]; - entry.path = fields[2]; - - entries[entry.controllers] = entry; - } - std::cerr << absl::StreamFormat("<end of %s>", path) << std::endl; - - return entries; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/cgroup_util.h b/test/util/cgroup_util.h deleted file mode 100644 index e3f696a89..000000000 --- a/test/util/cgroup_util.h +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2021 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_CGROUP_UTIL_H_ -#define GVISOR_TEST_UTIL_CGROUP_UTIL_H_ - -#include <unistd.h> - -#include "absl/container/flat_hash_map.h" -#include "absl/container/flat_hash_set.h" -#include "absl/strings/string_view.h" -#include "test/util/cleanup.h" -#include "test/util/fs_util.h" -#include "test/util/temp_path.h" - -namespace gvisor { -namespace testing { - -// Cgroup represents a cgroup directory on a mounted cgroupfs. -class Cgroup { - public: - Cgroup(std::string_view path); - - uint64_t id() const { return id_; } - - const std::string& Path() const { return cgroup_path_; } - - std::string Relpath(absl::string_view leaf) const { - return JoinPath(cgroup_path_, leaf); - } - - // Returns the contents of a cgroup control file with the given name. - PosixErrorOr<std::string> ReadControlFile(absl::string_view name) const; - - // Reads the contents of a cgroup control with the given name, and attempts - // to parse it as an integer. - PosixErrorOr<int64_t> ReadIntegerControlFile(absl::string_view name) const; - - // Writes a string to a cgroup control file. - PosixError WriteControlFile(absl::string_view name, - const std::string& value) const; - - // Writes an integer value to a cgroup control file. - PosixError WriteIntegerControlFile(absl::string_view name, - int64_t value) const; - - // Returns the thread ids of the leaders of thread groups managed by this - // cgroup. - PosixErrorOr<absl::flat_hash_set<pid_t>> Procs() const; - - PosixErrorOr<absl::flat_hash_set<pid_t>> Tasks() const; - - // ContainsCallingProcess checks whether the calling process is part of the - PosixError ContainsCallingProcess() const; - - private: - PosixErrorOr<absl::flat_hash_set<pid_t>> ParsePIDList( - absl::string_view data) const; - - static int64_t next_id_; - int64_t id_; - const std::string cgroup_path_; -}; - -// Mounter is a utility for creating cgroupfs mounts. It automatically manages -// the lifetime of created mounts. -class Mounter { - public: - Mounter(TempPath root) : root_(std::move(root)) {} - - PosixErrorOr<Cgroup> MountCgroupfs(std::string mopts); - - PosixError Unmount(const Cgroup& c); - - void release(const Cgroup& c); - - private: - // The destruction order of these members avoids errors during cleanup. We - // first unmount (by executing the mounts_ cleanups), then delete the - // mountpoint subdirs, then delete the root. - TempPath root_; - absl::flat_hash_map<int64_t, TempPath> mountpoints_; - absl::flat_hash_map<int64_t, Cleanup> mounts_; -}; - -// Represents a line from /proc/cgroups. -struct CgroupsEntry { - std::string subsys_name; - uint32_t hierarchy; - uint64_t num_cgroups; - bool enabled; -}; - -// Returns a parsed representation of /proc/cgroups. -PosixErrorOr<absl::flat_hash_map<std::string, CgroupsEntry>> -ProcCgroupsEntries(); - -// Represents a line from /proc/<pid>/cgroup. -struct PIDCgroupEntry { - uint32_t hierarchy; - std::string controllers; - std::string path; -}; - -// Returns a parsed representation of /proc/<pid>/cgroup. -PosixErrorOr<absl::flat_hash_map<std::string, PIDCgroupEntry>> -ProcPIDCgroupEntries(pid_t pid); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_CGROUP_UTIL_H_ diff --git a/test/util/cleanup.h b/test/util/cleanup.h deleted file mode 100644 index c76482ef4..000000000 --- a/test/util/cleanup.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_CLEANUP_H_ -#define GVISOR_TEST_UTIL_CLEANUP_H_ - -#include <functional> -#include <utility> - -namespace gvisor { -namespace testing { - -class Cleanup { - public: - Cleanup() : released_(true) {} - explicit Cleanup(std::function<void()>&& callback) : cb_(callback) {} - - Cleanup(Cleanup&& other) { - released_ = other.released_; - cb_ = other.Release(); - } - - Cleanup& operator=(Cleanup&& other) { - released_ = other.released_; - cb_ = other.Release(); - return *this; - } - - ~Cleanup() { - if (!released_) { - cb_(); - } - } - - std::function<void()>&& Release() { - released_ = true; - return std::move(cb_); - } - - private: - Cleanup(Cleanup const& other) = delete; - - bool released_ = false; - std::function<void(void)> cb_; -}; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_CLEANUP_H_ diff --git a/test/util/epoll_util.cc b/test/util/epoll_util.cc deleted file mode 100644 index 2e5051468..000000000 --- a/test/util/epoll_util.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/epoll_util.h" - -#include <sys/epoll.h> - -#include "gmock/gmock.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr<FileDescriptor> NewEpollFD(int size) { - // "Since Linux 2.6.8, the size argument is ignored, but must be greater than - // zero." - epoll_create(2) - int fd = epoll_create(size); - MaybeSave(); - if (fd < 0) { - return PosixError(errno, "epoll_create"); - } - return FileDescriptor(fd); -} - -PosixError RegisterEpollFD(int epoll_fd, int target_fd, int events, - uint64_t data) { - struct epoll_event event; - event.events = events; - event.data.u64 = data; - int rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, target_fd, &event); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "epoll_ctl"); - } - return NoError(); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/epoll_util.h b/test/util/epoll_util.h deleted file mode 100644 index f233b37d5..000000000 --- a/test/util/epoll_util.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_EPOLL_UTIL_H_ -#define GVISOR_TEST_UTIL_EPOLL_UTIL_H_ - -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -// Returns a new epoll file descriptor. -PosixErrorOr<FileDescriptor> NewEpollFD(int size = 1); - -// Registers `target_fd` with the epoll instance represented by `epoll_fd` for -// the epoll events `events`. Events on `target_fd` will be indicated by setting -// data.u64 to `data` in the returned epoll_event. -PosixError RegisterEpollFD(int epoll_fd, int target_fd, int events, - uint64_t data); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_EPOLL_UTIL_H_ diff --git a/test/util/eventfd_util.h b/test/util/eventfd_util.h deleted file mode 100644 index cb9ce829c..000000000 --- a/test/util/eventfd_util.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_EVENTFD_UTIL_H_ -#define GVISOR_TEST_UTIL_EVENTFD_UTIL_H_ - -#include <sys/eventfd.h> - -#include <cerrno> - -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" - -namespace gvisor { -namespace testing { - -// Returns a new eventfd with the given initial value and flags. -inline PosixErrorOr<FileDescriptor> NewEventFD(unsigned int initval = 0, - int flags = 0) { - int fd = eventfd(initval, flags); - MaybeSave(); - if (fd < 0) { - return PosixError(errno, "eventfd"); - } - return FileDescriptor(fd); -} - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_EVENTFD_UTIL_H_ diff --git a/test/util/file_descriptor.h b/test/util/file_descriptor.h deleted file mode 100644 index fc5caa55b..000000000 --- a/test/util/file_descriptor.h +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_FILE_DESCRIPTOR_H_ -#define GVISOR_TEST_UTIL_FILE_DESCRIPTOR_H_ - -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include <algorithm> -#include <string> - -#include "gmock/gmock.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_format.h" -#include "test/util/logging.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" - -namespace gvisor { -namespace testing { - -// FileDescriptor is an RAII type class which takes ownership of a file -// descriptor. It will close the FD when this object goes out of scope. -class FileDescriptor { - public: - // Constructs an empty FileDescriptor (one that does not own a file - // descriptor). - FileDescriptor() = default; - - // Constructs a FileDescriptor that owns fd. If fd is negative, constructs an - // empty FileDescriptor. - explicit FileDescriptor(int fd) { set_fd(fd); } - - FileDescriptor(FileDescriptor&& orig) : fd_(orig.release()) {} - - FileDescriptor& operator=(FileDescriptor&& orig) { - reset(orig.release()); - return *this; - } - - PosixErrorOr<FileDescriptor> Dup() const { - if (fd_ < 0) { - return PosixError(EINVAL, "Attempting to Dup unset fd"); - } - - int fd = dup(fd_); - if (fd < 0) { - return PosixError(errno, absl::StrCat("dup ", fd_)); - } - MaybeSave(); - return FileDescriptor(fd); - } - - FileDescriptor(FileDescriptor const& other) = delete; - FileDescriptor& operator=(FileDescriptor const& other) = delete; - - ~FileDescriptor() { reset(); } - - // If this object is non-empty, returns the owned file descriptor. (Ownership - // is retained by the FileDescriptor.) Otherwise returns -1. - int get() const { return fd_; } - - // If this object is non-empty, transfers ownership of the file descriptor to - // the caller and returns it. Otherwise returns -1. - int release() { - int const fd = fd_; - fd_ = -1; - return fd; - } - - // If this object is non-empty, closes the owned file descriptor (recording a - // test failure if the close fails). - void reset() { reset(-1); } - - // Like no-arg reset(), but the FileDescriptor takes ownership of fd after - // closing its existing file descriptor. - void reset(int fd) { - if (fd_ >= 0) { - TEST_PCHECK(close(fd_) == 0); - MaybeSave(); - } - set_fd(fd); - } - - private: - // Wrapper that coerces negative fd values other than -1 to -1 so that get() - // etc. return -1. - void set_fd(int fd) { fd_ = std::max(fd, -1); } - - int fd_ = -1; -}; - -// Wrapper around open(2) that returns a FileDescriptor. -inline PosixErrorOr<FileDescriptor> Open(std::string const& path, int flags, - mode_t mode = 0) { - int fd = open(path.c_str(), flags, mode); - if (fd < 0) { - return PosixError(errno, absl::StrFormat("open(%s, %#x, %#o)", path.c_str(), - flags, mode)); - } - MaybeSave(); - return FileDescriptor(fd); -} - -// Wrapper around openat(2) that returns a FileDescriptor. -inline PosixErrorOr<FileDescriptor> OpenAt(int dirfd, std::string const& path, - int flags, mode_t mode = 0) { - int fd = openat(dirfd, path.c_str(), flags, mode); - if (fd < 0) { - return PosixError(errno, absl::StrFormat("openat(%d, %s, %#x, %#o)", dirfd, - path, flags, mode)); - } - MaybeSave(); - return FileDescriptor(fd); -} - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_FILE_DESCRIPTOR_H_ diff --git a/test/util/fs_util.cc b/test/util/fs_util.cc deleted file mode 100644 index 483ae848d..000000000 --- a/test/util/fs_util.cc +++ /dev/null @@ -1,727 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/fs_util.h" - -#include <dirent.h> -#ifdef __linux__ -#include <linux/magic.h> -#endif // __linux__ -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gmock/gmock.h" -#include "absl/strings/match.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_split.h" -#include "absl/strings/string_view.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -namespace { -PosixError WriteContentsToFD(int fd, absl::string_view contents) { - int written = 0; - while (static_cast<absl::string_view::size_type>(written) < contents.size()) { - int wrote = write(fd, contents.data() + written, contents.size() - written); - if (wrote < 0) { - if (errno == EINTR) { - continue; - } - return PosixError( - errno, absl::StrCat("WriteContentsToFD fd: ", fd, " write failure.")); - } - written += wrote; - } - return NoError(); -} -} // namespace - -namespace internal { - -// Given a collection of file paths, append them all together, -// ensuring that the proper path separators are inserted between them. -std::string JoinPathImpl(std::initializer_list<absl::string_view> paths) { - std::string result; - - if (paths.size() != 0) { - // This size calculation is worst-case: it assumes one extra "/" for every - // path other than the first. - size_t total_size = paths.size() - 1; - for (const absl::string_view path : paths) total_size += path.size(); - result.resize(total_size); - - auto begin = result.begin(); - auto out = begin; - bool trailing_slash = false; - for (absl::string_view path : paths) { - if (path.empty()) continue; - if (path.front() == '/') { - if (trailing_slash) { - path.remove_prefix(1); - } - } else { - if (!trailing_slash && out != begin) *out++ = '/'; - } - const size_t this_size = path.size(); - memcpy(&*out, path.data(), this_size); - out += this_size; - trailing_slash = out[-1] == '/'; - } - result.erase(out - begin); - } - return result; -} -} // namespace internal - -// Returns a status or the current working directory. -PosixErrorOr<std::string> GetCWD() { - char buffer[PATH_MAX + 1] = {}; - if (getcwd(buffer, PATH_MAX) == nullptr) { - return PosixError(errno, "GetCWD() failed"); - } - - return std::string(buffer); -} - -PosixErrorOr<struct stat> Stat(absl::string_view path) { - struct stat stat_buf; - int res = stat(std::string(path).c_str(), &stat_buf); - if (res < 0) { - return PosixError(errno, absl::StrCat("stat ", path)); - } - return stat_buf; -} - -PosixErrorOr<struct stat> Lstat(absl::string_view path) { - struct stat stat_buf; - int res = lstat(std::string(path).c_str(), &stat_buf); - if (res < 0) { - return PosixError(errno, absl::StrCat("lstat ", path)); - } - return stat_buf; -} - -PosixErrorOr<struct stat> Fstat(int fd) { - struct stat stat_buf; - int res = fstat(fd, &stat_buf); - if (res < 0) { - return PosixError(errno, absl::StrCat("fstat ", fd)); - } - return stat_buf; -} - -PosixErrorOr<bool> Exists(absl::string_view path) { - struct stat stat_buf; - int res = lstat(std::string(path).c_str(), &stat_buf); - if (res < 0) { - if (errno == ENOENT) { - return false; - } - return PosixError(errno, absl::StrCat("lstat ", path)); - } - return true; -} - -PosixErrorOr<bool> IsDirectory(absl::string_view path) { - ASSIGN_OR_RETURN_ERRNO(struct stat stat_buf, Lstat(path)); - if (S_ISDIR(stat_buf.st_mode)) { - return true; - } - - return false; -} - -PosixError Delete(absl::string_view path) { - int res = unlink(std::string(path).c_str()); - if (res < 0) { - return PosixError(errno, absl::StrCat("unlink ", path)); - } - - return NoError(); -} - -PosixError Truncate(absl::string_view path, int length) { - int res = truncate(std::string(path).c_str(), length); - if (res < 0) { - return PosixError(errno, - absl::StrCat("truncate ", path, " to length ", length)); - } - - return NoError(); -} - -PosixError Chmod(absl::string_view path, int mode) { - int res = chmod(std::string(path).c_str(), mode); - if (res < 0) { - return PosixError(errno, absl::StrCat("chmod ", path)); - } - - return NoError(); -} - -PosixError MknodAt(const FileDescriptor& dfd, absl::string_view path, int mode, - dev_t dev) { - int res = mknodat(dfd.get(), std::string(path).c_str(), mode, dev); - if (res < 0) { - return PosixError(errno, absl::StrCat("mknod ", path)); - } - - return NoError(); -} - -PosixError UnlinkAt(const FileDescriptor& dfd, absl::string_view path, - int flags) { - int res = unlinkat(dfd.get(), std::string(path).c_str(), flags); - if (res < 0) { - return PosixError(errno, absl::StrCat("unlink ", path)); - } - - return NoError(); -} - -PosixError Mkdir(absl::string_view path, int mode) { - int res = mkdir(std::string(path).c_str(), mode); - if (res < 0) { - return PosixError(errno, absl::StrCat("mkdir ", path, " mode ", mode)); - } - - return NoError(); -} - -PosixError Rmdir(absl::string_view path) { - int res = rmdir(std::string(path).c_str()); - if (res < 0) { - return PosixError(errno, absl::StrCat("rmdir ", path)); - } - - return NoError(); -} - -PosixError SetContents(absl::string_view path, absl::string_view contents) { - ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(path)); - if (!exists) { - return PosixError( - ENOENT, absl::StrCat("SetContents file ", path, " doesn't exist.")); - } - - ASSIGN_OR_RETURN_ERRNO(auto fd, Open(std::string(path), O_WRONLY | O_TRUNC)); - return WriteContentsToFD(fd.get(), contents); -} - -// Create a file with the given contents (if it does not already exist with the -// given mode) and then set the contents. -PosixError CreateWithContents(absl::string_view path, - absl::string_view contents, int mode) { - ASSIGN_OR_RETURN_ERRNO( - auto fd, Open(std::string(path), O_WRONLY | O_CREAT | O_TRUNC, mode)); - return WriteContentsToFD(fd.get(), contents); -} - -PosixError GetContents(absl::string_view path, std::string* output) { - ASSIGN_OR_RETURN_ERRNO(auto fd, Open(std::string(path), O_RDONLY)); - output->clear(); - - // Keep reading until we hit an EOF or an error. - return GetContentsFD(fd.get(), output); -} - -PosixErrorOr<std::string> GetContents(absl::string_view path) { - std::string ret; - RETURN_IF_ERRNO(GetContents(path, &ret)); - return ret; -} - -PosixErrorOr<std::string> GetContentsFD(int fd) { - std::string ret; - RETURN_IF_ERRNO(GetContentsFD(fd, &ret)); - return ret; -} - -PosixError GetContentsFD(int fd, std::string* output) { - // Keep reading until we hit an EOF or an error. - while (true) { - char buf[16 * 1024] = {}; // Read in 16KB chunks. - int bytes_read = read(fd, buf, sizeof(buf)); - if (bytes_read < 0) { - if (errno == EINTR) { - continue; - } - return PosixError(errno, "GetContentsFD read failure."); - } - - if (bytes_read == 0) { - break; // EOF. - } - - output->append(buf, bytes_read); - } - return NoError(); -} - -PosixErrorOr<std::string> ReadLink(absl::string_view path) { - char buf[PATH_MAX + 1] = {}; - int ret = readlink(std::string(path).c_str(), buf, PATH_MAX); - if (ret < 0) { - return PosixError(errno, absl::StrCat("readlink ", path)); - } - - return std::string(buf, ret); -} - -PosixError WalkTree( - absl::string_view path, bool recursive, - const std::function<void(absl::string_view, const struct stat&)>& cb) { - DIR* dir = opendir(std::string(path).c_str()); - if (dir == nullptr) { - return PosixError(errno, absl::StrCat("opendir ", path)); - } - auto dir_closer = Cleanup([&dir]() { closedir(dir); }); - while (true) { - // Readdir(3): If the end of the directory stream is reached, NULL is - // returned and errno is not changed. If an error occurs, NULL is returned - // and errno is set appropriately. To distinguish end of stream and from an - // error, set errno to zero before calling readdir() and then check the - // value of errno if NULL is returned. - errno = 0; - struct dirent* dp = readdir(dir); - if (dp == nullptr) { - if (errno != 0) { - return PosixError(errno, absl::StrCat("readdir ", path)); - } - break; // We're done. - } - - if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) { - // Skip dots. - continue; - } - - auto full_path = JoinPath(path, dp->d_name); - ASSIGN_OR_RETURN_ERRNO(struct stat s, Stat(full_path)); - if (S_ISDIR(s.st_mode) && recursive) { - RETURN_IF_ERRNO(WalkTree(full_path, recursive, cb)); - } else { - cb(full_path, s); - } - } - // We're done walking so let's invoke our cleanup callback now. - dir_closer.Release()(); - - // And we have to dispatch the callback on the base directory. - ASSIGN_OR_RETURN_ERRNO(struct stat s, Stat(path)); - cb(path, s); - - return NoError(); -} - -PosixErrorOr<std::vector<std::string>> ListDir(absl::string_view abspath, - bool skipdots) { - std::vector<std::string> files; - - DIR* dir = opendir(std::string(abspath).c_str()); - if (dir == nullptr) { - return PosixError(errno, absl::StrCat("opendir ", abspath)); - } - auto dir_closer = Cleanup([&dir]() { closedir(dir); }); - while (true) { - // Readdir(3): If the end of the directory stream is reached, NULL is - // returned and errno is not changed. If an error occurs, NULL is returned - // and errno is set appropriately. To distinguish end of stream and from an - // error, set errno to zero before calling readdir() and then check the - // value of errno if NULL is returned. - errno = 0; - struct dirent* dp = readdir(dir); - if (dp == nullptr) { - if (errno != 0) { - return PosixError(errno, absl::StrCat("readdir ", abspath)); - } - break; // We're done. - } - - if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) { - if (skipdots) { - continue; - } - } - files.push_back(std::string(dp->d_name)); - } - - return files; -} - -PosixError DirContains(absl::string_view path, - const std::vector<std::string>& expect, - const std::vector<std::string>& exclude) { - ASSIGN_OR_RETURN_ERRNO(auto listing, ListDir(path, false)); - - for (auto& expected_entry : expect) { - auto cursor = std::find(listing.begin(), listing.end(), expected_entry); - if (cursor == listing.end()) { - return PosixError(ENOENT, absl::StrFormat("Failed to find '%s' in '%s'", - expected_entry, path)); - } - } - for (auto& excluded_entry : exclude) { - auto cursor = std::find(listing.begin(), listing.end(), excluded_entry); - if (cursor != listing.end()) { - return PosixError(ENOENT, absl::StrCat("File '", excluded_entry, - "' found in path '", path, "'")); - } - } - return NoError(); -} - -PosixError EventuallyDirContains(absl::string_view path, - const std::vector<std::string>& expect, - const std::vector<std::string>& exclude) { - constexpr int kRetryCount = 100; - const absl::Duration kRetryDelay = absl::Milliseconds(100); - - for (int i = 0; i < kRetryCount; ++i) { - auto res = DirContains(path, expect, exclude); - if (res.ok()) { - return res; - } - if (i < kRetryCount - 1) { - // Sleep if this isn't the final iteration. - absl::SleepFor(kRetryDelay); - } - } - return PosixError(ETIMEDOUT, - "Timed out while waiting for directory to contain files "); -} - -PosixError RecursivelyDelete(absl::string_view path, int* undeleted_dirs, - int* undeleted_files) { - ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(path)); - if (!exists) { - return PosixError(ENOENT, absl::StrCat(path, " does not exist")); - } - - ASSIGN_OR_RETURN_ERRNO(bool dir, IsDirectory(path)); - if (!dir) { - // Nothing recursive needs to happen we can just call Delete. - auto status = Delete(path); - if (!status.ok() && undeleted_files) { - (*undeleted_files)++; - } - return status; - } - - return WalkTree(path, /*recursive=*/true, - [&](absl::string_view absolute_path, const struct stat& s) { - if (S_ISDIR(s.st_mode)) { - auto rm_status = Rmdir(absolute_path); - if (!rm_status.ok() && undeleted_dirs) { - (*undeleted_dirs)++; - } - } else { - auto delete_status = Delete(absolute_path); - if (!delete_status.ok() && undeleted_files) { - (*undeleted_files)++; - } - } - }); -} - -PosixError RecursivelyCreateDir(absl::string_view path) { - if (path.empty() || path == "/") { - return PosixError(EINVAL, "Cannot create root!"); - } - - // Does it already exist, if so we're done. - ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(path)); - if (exists) { - return NoError(); - } - - // Do we need to create directories under us? - auto dirname = Dirname(path); - ASSIGN_OR_RETURN_ERRNO(exists, Exists(dirname)); - if (!exists) { - RETURN_IF_ERRNO(RecursivelyCreateDir(dirname)); - } - - return Mkdir(path); -} - -// Makes a path absolute with respect to an optional base. If no base is -// provided it will use the current working directory. -PosixErrorOr<std::string> MakeAbsolute(absl::string_view filename, - absl::string_view base) { - if (filename.empty()) { - return PosixError(EINVAL, "filename cannot be empty."); - } - - if (filename[0] == '/') { - // This path is already absolute. - return std::string(filename); - } - - std::string actual_base; - if (!base.empty()) { - actual_base = std::string(base); - } else { - auto cwd_or = GetCWD(); - RETURN_IF_ERRNO(cwd_or.error()); - actual_base = cwd_or.ValueOrDie(); - } - - // Reverse iterate removing trailing slashes, effectively right trim '/'. - for (int i = actual_base.size() - 1; i >= 0 && actual_base[i] == '/'; --i) { - actual_base.erase(i, 1); - } - - if (filename == ".") { - return actual_base.empty() ? "/" : actual_base; - } - - return absl::StrCat(actual_base, "/", filename); -} - -std::string CleanPath(const absl::string_view unclean_path) { - std::string path = std::string(unclean_path); - const char* src = path.c_str(); - std::string::iterator dst = path.begin(); - - // Check for absolute path and determine initial backtrack limit. - const bool is_absolute_path = *src == '/'; - if (is_absolute_path) { - *dst++ = *src++; - while (*src == '/') ++src; - } - std::string::const_iterator backtrack_limit = dst; - - // Process all parts - while (*src) { - bool parsed = false; - - if (src[0] == '.') { - // 1dot ".<whateverisnext>", check for END or SEP. - if (src[1] == '/' || !src[1]) { - if (*++src) { - ++src; - } - parsed = true; - } else if (src[1] == '.' && (src[2] == '/' || !src[2])) { - // 2dot END or SEP (".." | "../<whateverisnext>"). - src += 2; - if (dst != backtrack_limit) { - // We can backtrack the previous part - for (--dst; dst != backtrack_limit && dst[-1] != '/'; --dst) { - // Empty. - } - } else if (!is_absolute_path) { - // Failed to backtrack and we can't skip it either. Rewind and copy. - src -= 2; - *dst++ = *src++; - *dst++ = *src++; - if (*src) { - *dst++ = *src; - } - // We can never backtrack over a copied "../" part so set new limit. - backtrack_limit = dst; - } - if (*src) { - ++src; - } - parsed = true; - } - } - - // If not parsed, copy entire part until the next SEP or EOS. - if (!parsed) { - while (*src && *src != '/') { - *dst++ = *src++; - } - if (*src) { - *dst++ = *src++; - } - } - - // Skip consecutive SEP occurrences - while (*src == '/') { - ++src; - } - } - - // Calculate and check the length of the cleaned path. - int path_length = dst - path.begin(); - if (path_length != 0) { - // Remove trailing '/' except if it is root path ("/" ==> path_length := 1) - if (path_length > 1 && path[path_length - 1] == '/') { - --path_length; - } - path.resize(path_length); - } else { - // The cleaned path is empty; assign "." as per the spec. - path.assign(1, '.'); - } - return path; -} - -PosixErrorOr<std::string> GetRelativePath(absl::string_view source, - absl::string_view dest) { - if (!absl::StartsWith(source, "/") || !absl::StartsWith(dest, "/")) { - // At least one of the inputs is not an absolute path. - return PosixError( - EINVAL, - "GetRelativePath: At least one of the inputs is not an absolute path."); - } - const std::string clean_source = CleanPath(source); - const std::string clean_dest = CleanPath(dest); - auto source_parts = absl::StrSplit(clean_source, '/', absl::SkipEmpty()); - auto dest_parts = absl::StrSplit(clean_dest, '/', absl::SkipEmpty()); - auto source_iter = source_parts.begin(); - auto dest_iter = dest_parts.begin(); - - // Advance past common prefix. - while (source_iter != source_parts.end() && dest_iter != dest_parts.end() && - *source_iter == *dest_iter) { - ++source_iter; - ++dest_iter; - } - - // Build result backtracking. - std::string result = ""; - while (source_iter != source_parts.end()) { - absl::StrAppend(&result, "../"); - ++source_iter; - } - - // Add remaining path to dest. - while (dest_iter != dest_parts.end()) { - absl::StrAppend(&result, *dest_iter, "/"); - ++dest_iter; - } - - if (result.empty()) { - return std::string("."); - } - - // Remove trailing slash. - result.erase(result.size() - 1); - return result; -} - -absl::string_view Dirname(absl::string_view path) { - return SplitPath(path).first; -} - -absl::string_view Basename(absl::string_view path) { - return SplitPath(path).second; -} - -std::pair<absl::string_view, absl::string_view> SplitPath( - absl::string_view path) { - std::string::size_type pos = path.find_last_of('/'); - - // Handle the case with no '/' in 'path'. - if (pos == absl::string_view::npos) { - return std::make_pair(path.substr(0, 0), path); - } - - // Handle the case with a single leading '/' in 'path'. - if (pos == 0) { - return std::make_pair(path.substr(0, 1), absl::ClippedSubstr(path, 1)); - } - - return std::make_pair(path.substr(0, pos), - absl::ClippedSubstr(path, pos + 1)); -} - -std::string JoinPath(absl::string_view path1, absl::string_view path2) { - if (path1.empty()) { - return std::string(path2); - } - if (path2.empty()) { - return std::string(path1); - } - - if (path1.back() == '/') { - if (path2.front() == '/') { - return absl::StrCat(path1, absl::ClippedSubstr(path2, 1)); - } - } else { - if (path2.front() != '/') { - return absl::StrCat(path1, "/", path2); - } - } - return absl::StrCat(path1, path2); -} - -PosixErrorOr<std::string> ProcessExePath(int pid) { - if (pid <= 0) { - return PosixError(EINVAL, "Invalid pid specified"); - } - - return ReadLink(absl::StrCat("/proc/", pid, "/exe")); -} - -#ifdef __linux__ -PosixErrorOr<bool> IsTmpfs(const std::string& path) { - struct statfs stat; - if (statfs(path.c_str(), &stat)) { - if (errno == ENOENT) { - // Nothing at path, don't raise this as an error. Instead, just report no - // tmpfs at path. - return false; - } - return PosixError(errno, - absl::StrFormat("statfs(\"%s\", %#p)", path, &stat)); - } - return stat.f_type == TMPFS_MAGIC; -} -#endif // __linux__ - -PosixErrorOr<bool> IsOverlayfs(const std::string& path) { - struct statfs stat; - if (statfs(path.c_str(), &stat)) { - if (errno == ENOENT) { - // Nothing at path, don't raise this as an error. Instead, just report no - // overlayfs at path. - return false; - } - return PosixError(errno, - absl::StrFormat("statfs(\"%s\", %#p)", path, &stat)); - } - return stat.f_type == OVERLAYFS_SUPER_MAGIC; -} - -PosixError CheckSameFile(const FileDescriptor& fd1, const FileDescriptor& fd2) { - struct stat stat_result1, stat_result2; - int res = fstat(fd1.get(), &stat_result1); - if (res < 0) { - return PosixError(errno, absl::StrCat("fstat ", fd1.get())); - } - - res = fstat(fd2.get(), &stat_result2); - if (res < 0) { - return PosixError(errno, absl::StrCat("fstat ", fd2.get())); - } - EXPECT_EQ(stat_result1.st_dev, stat_result2.st_dev); - EXPECT_EQ(stat_result1.st_ino, stat_result2.st_ino); - - return NoError(); -} -} // namespace testing -} // namespace gvisor diff --git a/test/util/fs_util.h b/test/util/fs_util.h deleted file mode 100644 index bb2d1d3c8..000000000 --- a/test/util/fs_util.h +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_FS_UTIL_H_ -#define GVISOR_TEST_UTIL_FS_UTIL_H_ - -#include <dirent.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/types.h> -#include <unistd.h> - -#include "absl/strings/string_view.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -// O_LARGEFILE as defined by Linux. glibc tries to be clever by setting it to 0 -// because "it isn't needed", even though Linux can return it via F_GETFL. -#if defined(__x86_64__) -constexpr int kOLargeFile = 00100000; -#elif defined(__aarch64__) -constexpr int kOLargeFile = 00400000; -#else -#error "Unknown architecture" -#endif - -// From linux/magic.h. For some reason, not defined in the headers for some -// build environments. -#define OVERLAYFS_SUPER_MAGIC 0x794c7630 - -// Returns a status or the current working directory. -PosixErrorOr<std::string> GetCWD(); - -// Returns true/false depending on whether or not path exists, or an error if it -// can't be determined. -PosixErrorOr<bool> Exists(absl::string_view path); - -// Returns a stat structure for the given path or an error. If the path -// represents a symlink, it will be traversed. -PosixErrorOr<struct stat> Stat(absl::string_view path); - -// Returns a stat structure for the given path or an error. If the path -// represents a symlink, it will not be traversed. -PosixErrorOr<struct stat> Lstat(absl::string_view path); - -// Returns a stat struct for the given fd. -PosixErrorOr<struct stat> Fstat(int fd); - -// Deletes the file or directory at path or returns an error. -PosixError Delete(absl::string_view path); - -// Changes the mode of a file or returns an error. -PosixError Chmod(absl::string_view path, int mode); - -// Create a special or ordinary file. -PosixError MknodAt(const FileDescriptor& dfd, absl::string_view path, int mode, - dev_t dev); - -// Unlink the file. -PosixError UnlinkAt(const FileDescriptor& dfd, absl::string_view path, - int flags); - -// Truncates a file to the given length or returns an error. -PosixError Truncate(absl::string_view path, int length); - -// Returns true/false depending on whether or not the path is a directory or -// returns an error. -PosixErrorOr<bool> IsDirectory(absl::string_view path); - -// Makes a directory or returns an error. -PosixError Mkdir(absl::string_view path, int mode = 0755); - -// Removes a directory or returns an error. -PosixError Rmdir(absl::string_view path); - -// Attempts to set the contents of a file or returns an error. -PosixError SetContents(absl::string_view path, absl::string_view contents); - -// Creates a file with the given contents and mode or returns an error. -PosixError CreateWithContents(absl::string_view path, - absl::string_view contents, int mode = 0666); - -// Attempts to read the entire contents of the file into the provided string -// buffer or returns an error. -PosixError GetContents(absl::string_view path, std::string* output); - -// Attempts to read the entire contents of the file or returns an error. -PosixErrorOr<std::string> GetContents(absl::string_view path); - -// Attempts to read the entire contents of the provided fd into the provided -// string or returns an error. -PosixError GetContentsFD(int fd, std::string* output); - -// Attempts to read the entire contents of the provided fd or returns an error. -PosixErrorOr<std::string> GetContentsFD(int fd); - -// Executes the readlink(2) system call or returns an error. -PosixErrorOr<std::string> ReadLink(absl::string_view path); - -// WalkTree will walk a directory tree in a depth first search manner (if -// recursive). It will invoke a provided callback for each file and directory, -// the parent will always be invoked last making this appropriate for things -// such as deleting an entire directory tree. -// -// This method will return an error when it's unable to access the provided -// path, or when the path is not a directory. -PosixError WalkTree( - absl::string_view path, bool recursive, - const std::function<void(absl::string_view, const struct stat&)>& cb); - -// Returns the base filenames for all files under a given absolute path. If -// skipdots is true the returned vector will not contain "." or "..". This -// method does not walk the tree recursively it only returns the elements -// in that directory. -PosixErrorOr<std::vector<std::string>> ListDir(absl::string_view abspath, - bool skipdots); - -// Check that a directory contains children nodes named in expect, and does not -// contain any children nodes named in exclude. -PosixError DirContains(absl::string_view path, - const std::vector<std::string>& expect, - const std::vector<std::string>& exclude); - -// Same as DirContains, but adds a retry. Suitable for checking a directory -// being modified asynchronously. -PosixError EventuallyDirContains(absl::string_view path, - const std::vector<std::string>& expect, - const std::vector<std::string>& exclude); - -// Attempt to recursively delete a directory or file. Returns an error and -// the number of undeleted directories and files. If either -// undeleted_dirs or undeleted_files is nullptr then it will not be used. -PosixError RecursivelyDelete(absl::string_view path, int* undeleted_dirs, - int* undeleted_files); - -// Recursively create the directory provided or return an error. -PosixError RecursivelyCreateDir(absl::string_view path); - -// Makes a path absolute with respect to an optional base. If no base is -// provided it will use the current working directory. -PosixErrorOr<std::string> MakeAbsolute(absl::string_view filename, - absl::string_view base); - -// Generates a relative path from the source directory to the destination -// (dest) file or directory. This uses ../ when necessary for destinations -// which are not nested within the source. Both source and dest are required -// to be absolute paths, and an empty string will be returned if they are not. -PosixErrorOr<std::string> GetRelativePath(absl::string_view source, - absl::string_view dest); - -// Returns the part of the path before the final "/", EXCEPT: -// * If there is a single leading "/" in the path, the result will be the -// leading "/". -// * If there is no "/" in the path, the result is the empty prefix of the -// input string. -absl::string_view Dirname(absl::string_view path); - -// Return the parts of the path, split on the final "/". If there is no -// "/" in the path, the first part of the output is empty and the second -// is the input. If the only "/" in the path is the first character, it is -// the first part of the output. -std::pair<absl::string_view, absl::string_view> SplitPath( - absl::string_view path); - -// Returns the part of the path after the final "/". If there is no -// "/" in the path, the result is the same as the input. -// Note that this function's behavior differs from the Unix basename -// command if path ends with "/". For such paths, this function returns the -// empty string. -absl::string_view Basename(absl::string_view path); - -// Collapse duplicate "/"s, resolve ".." and "." path elements, remove -// trailing "/". -// -// NOTE: This respects relative vs. absolute paths, but does not -// invoke any system calls (getcwd(2)) in order to resolve relative -// paths wrt actual working directory. That is, this is purely a -// string manipulation, completely independent of process state. -std::string CleanPath(absl::string_view path); - -// Returns the full path to the executable of the given pid or a PosixError. -PosixErrorOr<std::string> ProcessExePath(int pid); - -#ifdef __linux__ -// IsTmpfs returns true if the file at path is backed by tmpfs. -PosixErrorOr<bool> IsTmpfs(const std::string& path); -#endif // __linux__ - -// IsOverlayfs returns true if the file at path is backed by overlayfs. -PosixErrorOr<bool> IsOverlayfs(const std::string& path); - -PosixError CheckSameFile(const FileDescriptor& fd1, const FileDescriptor& fd2); - -namespace internal { -// Not part of the public API. -std::string JoinPathImpl(std::initializer_list<absl::string_view> paths); -} // namespace internal - -// Join multiple paths together. -// All paths will be treated as relative paths, regardless of whether or not -// they start with a leading '/'. That is, all paths will be concatenated -// together, with the appropriate path separator inserted in between. -// Arguments must be convertible to absl::string_view. -// -// Usage: -// std::string path = JoinPath("/foo", dirname, filename); -// std::string path = JoinPath(FLAGS_test_srcdir, filename); -// -// 0, 1, 2-path specializations exist to optimize common cases. -inline std::string JoinPath() { return std::string(); } -inline std::string JoinPath(absl::string_view path) { - return std::string(path.data(), path.size()); -} - -std::string JoinPath(absl::string_view path1, absl::string_view path2); -template <typename... T> -inline std::string JoinPath(absl::string_view path1, absl::string_view path2, - absl::string_view path3, const T&... args) { - return internal::JoinPathImpl({path1, path2, path3, args...}); -} -} // namespace testing -} // namespace gvisor -#endif // GVISOR_TEST_UTIL_FS_UTIL_H_ diff --git a/test/util/fs_util_test.cc b/test/util/fs_util_test.cc deleted file mode 100644 index 657b6a46e..000000000 --- a/test/util/fs_util_test.cc +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/fs_util.h" - -#include <errno.h> - -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(FsUtilTest, RecursivelyCreateDirManualDelete) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string base_path = - JoinPath(root.path(), "/a/b/c/d/e/f/g/h/i/j/k/l/m"); - - ASSERT_THAT(Exists(base_path), IsPosixErrorOkAndHolds(false)); - ASSERT_NO_ERRNO(RecursivelyCreateDir(base_path)); - - // Delete everything until we hit root and then stop, we want to try this - // without using RecursivelyDelete. - std::string cur_path = base_path; - while (cur_path != root.path()) { - ASSERT_THAT(Exists(cur_path), IsPosixErrorOkAndHolds(true)); - ASSERT_NO_ERRNO(Rmdir(cur_path)); - ASSERT_THAT(Exists(cur_path), IsPosixErrorOkAndHolds(false)); - auto dir = Dirname(cur_path); - cur_path = std::string(dir); - } -} - -TEST(FsUtilTest, RecursivelyCreateAndDeleteDir) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string base_path = - JoinPath(root.path(), "/a/b/c/d/e/f/g/h/i/j/k/l/m"); - - ASSERT_THAT(Exists(base_path), IsPosixErrorOkAndHolds(false)); - ASSERT_NO_ERRNO(RecursivelyCreateDir(base_path)); - - const std::string sub_path = JoinPath(root.path(), "a"); - ASSERT_NO_ERRNO(RecursivelyDelete(sub_path, nullptr, nullptr)); - ASSERT_THAT(Exists(sub_path), IsPosixErrorOkAndHolds(false)); -} - -TEST(FsUtilTest, RecursivelyCreateAndDeletePartial) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string base_path = - JoinPath(root.path(), "/a/b/c/d/e/f/g/h/i/j/k/l/m"); - - ASSERT_THAT(Exists(base_path), IsPosixErrorOkAndHolds(false)); - ASSERT_NO_ERRNO(RecursivelyCreateDir(base_path)); - - const std::string a = JoinPath(root.path(), "a"); - auto listing = ASSERT_NO_ERRNO_AND_VALUE(ListDir(a, true)); - ASSERT_THAT(listing, ::testing::Contains("b")); - ASSERT_EQ(listing.size(), 1); - - listing = ASSERT_NO_ERRNO_AND_VALUE(ListDir(a, false)); - ASSERT_THAT(listing, ::testing::Contains(".")); - ASSERT_THAT(listing, ::testing::Contains("..")); - ASSERT_THAT(listing, ::testing::Contains("b")); - ASSERT_EQ(listing.size(), 3); - - const std::string sub_path = JoinPath(root.path(), "/a/b/c/d/e/f"); - - ASSERT_NO_ERRNO( - CreateWithContents(JoinPath(Dirname(sub_path), "file"), "Hello World")); - std::string contents = ""; - ASSERT_NO_ERRNO(GetContents(JoinPath(Dirname(sub_path), "file"), &contents)); - ASSERT_EQ(contents, "Hello World"); - - ASSERT_NO_ERRNO(RecursivelyDelete(sub_path, nullptr, nullptr)); - ASSERT_THAT(Exists(sub_path), IsPosixErrorOkAndHolds(false)); - - // The parent of the subpath (directory e) should still exist. - ASSERT_THAT(Exists(Dirname(sub_path)), IsPosixErrorOkAndHolds(true)); - - // The file we created along side f should also still exist. - ASSERT_THAT(Exists(JoinPath(Dirname(sub_path), "file")), - IsPosixErrorOkAndHolds(true)); -} -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/util/fuse_util.cc b/test/util/fuse_util.cc deleted file mode 100644 index 027f8386c..000000000 --- a/test/util/fuse_util.cc +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2020 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/fuse_util.h" - -#include <sys/stat.h> -#include <sys/types.h> - -#include <string> - -namespace gvisor { -namespace testing { - -// Create a default FuseAttr struct with specified mode, inode, and size. -fuse_attr DefaultFuseAttr(mode_t mode, uint64_t inode, uint64_t size) { - const int time_sec = 1595436289; - const int time_nsec = 134150844; - return (struct fuse_attr){ - .ino = inode, - .size = size, - .blocks = 4, - .atime = time_sec, - .mtime = time_sec, - .ctime = time_sec, - .atimensec = time_nsec, - .mtimensec = time_nsec, - .ctimensec = time_nsec, - .mode = mode, - .nlink = 2, - .uid = 1234, - .gid = 4321, - .rdev = 12, - .blksize = 4096, - }; -} - -// Create response body with specified mode, nodeID, and size. -fuse_entry_out DefaultEntryOut(mode_t mode, uint64_t node_id, uint64_t size) { - struct fuse_entry_out default_entry_out = { - .nodeid = node_id, - .generation = 0, - .entry_valid = 0, - .attr_valid = 0, - .entry_valid_nsec = 0, - .attr_valid_nsec = 0, - .attr = DefaultFuseAttr(mode, node_id, size), - }; - return default_entry_out; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/fuse_util.h b/test/util/fuse_util.h deleted file mode 100644 index 544fe1b38..000000000 --- a/test/util/fuse_util.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2020 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_FUSE_UTIL_H_ -#define GVISOR_TEST_UTIL_FUSE_UTIL_H_ - -#include <linux/fuse.h> -#include <sys/uio.h> - -#include <string> -#include <vector> - -namespace gvisor { -namespace testing { - -// The fundamental generation function with a single argument. If passed by -// std::string or std::vector<char>, it will call specialized versions as -// implemented below. -template <typename T> -std::vector<struct iovec> FuseGenerateIovecs(T &first) { - return {(struct iovec){.iov_base = &first, .iov_len = sizeof(first)}}; -} - -// If an argument is of type std::string, it must be used in read-only scenario. -// Because we are setting up iovec, which contains the original address of a -// data structure, we have to drop const qualification. Usually used with -// variable-length payload data. -template <typename T = std::string> -std::vector<struct iovec> FuseGenerateIovecs(std::string &first) { - // Pad one byte for null-terminate c-string. - return {(struct iovec){.iov_base = const_cast<char *>(first.c_str()), - .iov_len = first.size() + 1}}; -} - -// If an argument is of type std::vector<char>, it must be used in write-only -// scenario and the size of the variable must be greater than or equal to the -// size of the expected data. Usually used with variable-length payload data. -template <typename T = std::vector<char>> -std::vector<struct iovec> FuseGenerateIovecs(std::vector<char> &first) { - return {(struct iovec){.iov_base = first.data(), .iov_len = first.size()}}; -} - -// A helper function to set up an array of iovec struct for testing purpose. -// Use variadic class template to generalize different numbers and different -// types of FUSE structs. -template <typename T, typename... Types> -std::vector<struct iovec> FuseGenerateIovecs(T &first, Types &...args) { - auto first_iovec = FuseGenerateIovecs(first); - auto iovecs = FuseGenerateIovecs(args...); - first_iovec.insert(std::end(first_iovec), std::begin(iovecs), - std::end(iovecs)); - return first_iovec; -} - -// Create a fuse_attr filled with the specified mode and inode. -fuse_attr DefaultFuseAttr(mode_t mode, uint64_t inode, uint64_t size = 512); - -// Return a fuse_entry_out FUSE server response body. -fuse_entry_out DefaultEntryOut(mode_t mode, uint64_t node_id, - uint64_t size = 512); - -} // namespace testing -} // namespace gvisor -#endif // GVISOR_TEST_UTIL_FUSE_UTIL_H_ diff --git a/test/util/logging.cc b/test/util/logging.cc deleted file mode 100644 index 5fadb076b..000000000 --- a/test/util/logging.cc +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/logging.h" - -#include <errno.h> -#include <stdint.h> -#include <stdlib.h> -#include <unistd.h> - -namespace gvisor { -namespace testing { - -namespace { - -// We implement this here instead of using test_util to avoid cyclic -// dependencies. -int Write(int fd, const char* buf, size_t size) { - size_t written = 0; - while (written < size) { - int res = write(fd, buf + written, size - written); - if (res < 0 && errno == EINTR) { - continue; - } else if (res <= 0) { - break; - } - - written += res; - } - return static_cast<int>(written); -} - -// Write 32-bit decimal number to fd. -int WriteNumber(int fd, uint32_t val) { - constexpr char kDigits[] = "0123456789"; - constexpr int kBase = 10; - - // 10 chars for 32-bit number in decimal, 1 char for the NUL-terminator. - constexpr int kBufferSize = 11; - char buf[kBufferSize]; - - // Convert the number to string. - char* s = buf + sizeof(buf) - 1; - size_t size = 0; - - *s = '\0'; - do { - s--; - size++; - - *s = kDigits[val % kBase]; - val /= kBase; - } while (val); - - return Write(fd, s, size); -} - -} // namespace - -void CheckFailure(const char* cond, size_t cond_size, const char* msg, - size_t msg_size, int errno_value) { - constexpr char kCheckFailure[] = "Check failed: "; - Write(2, kCheckFailure, sizeof(kCheckFailure) - 1); - Write(2, cond, cond_size); - - if (msg != nullptr) { - Write(2, ": ", 2); - Write(2, msg, msg_size); - } - - if (errno_value != 0) { - constexpr char kErrnoMessage[] = " (errno "; - Write(2, kErrnoMessage, sizeof(kErrnoMessage) - 1); - WriteNumber(2, errno_value); - Write(2, ")", 1); - } - - Write(2, "\n", 1); - - abort(); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/logging.h b/test/util/logging.h deleted file mode 100644 index 5c17f1233..000000000 --- a/test/util/logging.h +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_LOGGING_H_ -#define GVISOR_TEST_UTIL_LOGGING_H_ - -#include <stddef.h> - -namespace gvisor { -namespace testing { - -void CheckFailure(const char* cond, size_t cond_size, const char* msg, - size_t msg_size, int errno_value); - -// If cond is false, aborts the current process. -// -// This macro is async-signal-safe. -#define TEST_CHECK(cond) \ - do { \ - if (!(cond)) { \ - ::gvisor::testing::CheckFailure(#cond, sizeof(#cond) - 1, nullptr, \ - 0, 0); \ - } \ - } while (0) - -// If cond is false, logs msg then aborts the current process. -// -// This macro is async-signal-safe. -#define TEST_CHECK_MSG(cond, msg) \ - do { \ - if (!(cond)) { \ - ::gvisor::testing::CheckFailure(#cond, sizeof(#cond) - 1, msg, \ - sizeof(msg) - 1, 0); \ - } \ - } while (0) - -// If cond is false, logs errno, then aborts the current process. -// -// This macro is async-signal-safe. -#define TEST_PCHECK(cond) \ - do { \ - if (!(cond)) { \ - ::gvisor::testing::CheckFailure(#cond, sizeof(#cond) - 1, nullptr, \ - 0, errno); \ - } \ - } while (0) - -// If cond is false, logs msg and errno, then aborts the current process. -// -// This macro is async-signal-safe. -#define TEST_PCHECK_MSG(cond, msg) \ - do { \ - if (!(cond)) { \ - ::gvisor::testing::CheckFailure(#cond, sizeof(#cond) - 1, msg, \ - sizeof(msg) - 1, errno); \ - } \ - } while (0) - -// expr must return PosixErrorOr<T>. The current process is aborted if -// !PosixError<T>.ok(). -// -// This macro is async-signal-safe. -#define TEST_CHECK_NO_ERRNO(expr) \ - ({ \ - auto _expr_result = (expr); \ - if (!_expr_result.ok()) { \ - ::gvisor::testing::CheckFailure( \ - #expr, sizeof(#expr) - 1, nullptr, 0, \ - _expr_result.error().errno_value()); \ - } \ - }) - -// expr must return PosixErrorOr<T>. The current process is aborted if -// !PosixError<T>.ok(). Otherwise, PosixErrorOr<T> value is returned. -// -// This macro is async-signal-safe. -#define TEST_CHECK_NO_ERRNO_AND_VALUE(expr) \ - ({ \ - auto _expr_result = (expr); \ - if (!_expr_result.ok()) { \ - ::gvisor::testing::CheckFailure( \ - #expr, sizeof(#expr) - 1, nullptr, 0, \ - _expr_result.error().errno_value()); \ - } \ - std::move(_expr_result).ValueOrDie(); \ - }) - -// cond must be greater or equal than 0. Used to test result of syscalls. -// -// This macro is async-signal-safe. -#define TEST_CHECK_SUCCESS(cond) TEST_PCHECK((cond) >= 0) - -// cond must be -1 and errno must match errno_value. Used to test errors from -// syscalls. -// -// This macro is async-signal-safe. -#define TEST_CHECK_ERRNO(cond, errno_value) \ - do { \ - TEST_PCHECK((cond) == -1); \ - TEST_PCHECK_MSG(errno == (errno_value), #cond " expected " #errno_value); \ - } while (0) - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_LOGGING_H_ diff --git a/test/util/memory_util.h b/test/util/memory_util.h deleted file mode 100644 index e189b73e8..000000000 --- a/test/util/memory_util.h +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_MEMORY_UTIL_H_ -#define GVISOR_TEST_UTIL_MEMORY_UTIL_H_ - -#include <errno.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/mman.h> - -#include "absl/strings/str_format.h" -#include "absl/strings/string_view.h" -#include "test/util/logging.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -// RAII type for mmap'ed memory. Only usable in tests due to use of a test-only -// macro that can't be named without invoking the presubmit's wrath. -class Mapping { - public: - // Constructs a mapping that owns nothing. - Mapping() = default; - - // Constructs a mapping that owns the mmapped memory [ptr, ptr+len). Most - // users should use Mmap or MmapAnon instead. - Mapping(void* ptr, size_t len) : ptr_(ptr), len_(len) {} - - Mapping(Mapping&& orig) : ptr_(orig.ptr_), len_(orig.len_) { orig.release(); } - - Mapping& operator=(Mapping&& orig) { - ptr_ = orig.ptr_; - len_ = orig.len_; - orig.release(); - return *this; - } - - Mapping(Mapping const&) = delete; - Mapping& operator=(Mapping const&) = delete; - - ~Mapping() { reset(); } - - void* ptr() const { return ptr_; } - size_t len() const { return len_; } - - // Returns a pointer to the end of the mapping. Useful for when the mapping - // is used as a thread stack. - void* endptr() const { return reinterpret_cast<void*>(addr() + len_); } - - // Returns the start of this mapping cast to uintptr_t for ease of pointer - // arithmetic. - uintptr_t addr() const { return reinterpret_cast<uintptr_t>(ptr_); } - - // Returns the end of this mapping cast to uintptr_t for ease of pointer - // arithmetic. - uintptr_t endaddr() const { return reinterpret_cast<uintptr_t>(endptr()); } - - // Returns this mapping as a StringPiece for ease of comparison. - // - // This function is named view in anticipation of the eventual replacement of - // StringPiece with std::string_view. - absl::string_view view() const { - return absl::string_view(static_cast<char const*>(ptr_), len_); - } - - // These are both named reset for consistency with standard smart pointers. - - void reset(void* ptr, size_t len) { - if (len_) { - TEST_PCHECK(munmap(ptr_, len_) == 0); - } - ptr_ = ptr; - len_ = len; - } - - void reset() { reset(nullptr, 0); } - - void release() { - ptr_ = nullptr; - len_ = 0; - } - - private: - void* ptr_ = nullptr; - size_t len_ = 0; -}; - -// Wrapper around mmap(2) that returns a Mapping. -inline PosixErrorOr<Mapping> Mmap(void* addr, size_t length, int prot, - int flags, int fd, off_t offset) { - void* ptr = mmap(addr, length, prot, flags, fd, offset); - if (ptr == MAP_FAILED) { - return PosixError( - errno, absl::StrFormat("mmap(%p, %d, %x, %x, %d, %d)", addr, length, - prot, flags, fd, offset)); - } - MaybeSave(); - return Mapping(ptr, length); -} - -// Convenience wrapper around Mmap for anonymous mappings. -inline PosixErrorOr<Mapping> MmapAnon(size_t length, int prot, int flags) { - return Mmap(nullptr, length, prot, flags | MAP_ANONYMOUS, -1, 0); -} - -// Wrapper for mremap that returns a PosixErrorOr<>, since the return type of -// void* isn't directly compatible with SyscallSucceeds. -inline PosixErrorOr<void*> Mremap(void* old_address, size_t old_size, - size_t new_size, int flags, - void* new_address) { - void* rv = mremap(old_address, old_size, new_size, flags, new_address); - if (rv == MAP_FAILED) { - return PosixError(errno, "mremap failed"); - } - return rv; -} - -// Returns true if the page containing addr is mapped. -inline bool IsMapped(uintptr_t addr) { - int const rv = msync(reinterpret_cast<void*>(addr & ~(kPageSize - 1)), - kPageSize, MS_ASYNC); - if (rv == 0) { - return true; - } - TEST_PCHECK_MSG(errno == ENOMEM, "msync failed with unexpected errno"); - return false; -} - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_MEMORY_UTIL_H_ diff --git a/test/util/mount_util.cc b/test/util/mount_util.cc deleted file mode 100644 index 48640d6a1..000000000 --- a/test/util/mount_util.cc +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2021 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/mount_util.h" - -#include <sys/syscall.h> -#include <unistd.h> - -#include "absl/strings/numbers.h" -#include "absl/strings/str_split.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr<std::vector<ProcMountsEntry>> ProcSelfMountsEntries() { - std::string content; - RETURN_IF_ERRNO(GetContents("/proc/self/mounts", &content)); - return ProcSelfMountsEntriesFrom(content); -} - -PosixErrorOr<std::vector<ProcMountsEntry>> ProcSelfMountsEntriesFrom( - const std::string& content) { - std::vector<ProcMountsEntry> entries; - std::vector<std::string> lines = - absl::StrSplit(content, absl::ByChar('\n'), absl::AllowEmpty()); - std::cerr << "<contents of /proc/self/mounts>" << std::endl; - for (const std::string& line : lines) { - std::cerr << line << std::endl; - if (line.empty()) { - continue; - } - - // Parse a single entry from /proc/self/mounts. - // - // Example entries: - // - // sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0 - // proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0 - // ^ ^ ^ ^ ^ ^ - // 0 1 2 3 4 5 - - ProcMountsEntry entry; - std::vector<std::string> fields = - absl::StrSplit(line, absl::ByChar(' '), absl::AllowEmpty()); - if (fields.size() != 6) { - return PosixError( - EINVAL, absl::StrFormat("Not enough tokens, got %d, content: <<%s>>", - fields.size(), content)); - } - - entry.spec = fields[0]; - entry.mount_point = fields[1]; - entry.fstype = fields[2]; - entry.mount_opts = fields[3]; - ASSIGN_OR_RETURN_ERRNO(entry.dump, Atoi<uint32_t>(fields[4])); - ASSIGN_OR_RETURN_ERRNO(entry.fsck, Atoi<uint32_t>(fields[5])); - - entries.push_back(entry); - } - std::cerr << "<end of /proc/self/mounts>" << std::endl; - - return entries; -} - -PosixErrorOr<std::vector<ProcMountInfoEntry>> ProcSelfMountInfoEntries() { - std::string content; - RETURN_IF_ERRNO(GetContents("/proc/self/mountinfo", &content)); - return ProcSelfMountInfoEntriesFrom(content); -} - -PosixErrorOr<std::vector<ProcMountInfoEntry>> ProcSelfMountInfoEntriesFrom( - const std::string& content) { - std::vector<ProcMountInfoEntry> entries; - std::vector<std::string> lines = - absl::StrSplit(content, absl::ByChar('\n'), absl::AllowEmpty()); - std::cerr << "<contents of /proc/self/mountinfo>" << std::endl; - for (const std::string& line : lines) { - std::cerr << line << std::endl; - if (line.empty()) { - continue; - } - - // Parse a single entry from /proc/self/mountinfo. - // - // Example entries: - // - // 22 28 0:20 / /sys rw,relatime shared:7 - sysfs sysfs rw - // 23 28 0:21 / /proc rw,relatime shared:14 - proc proc rw - // ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ - // 0 1 2 3 4 5 6 7 8 9 10 - - ProcMountInfoEntry entry; - std::vector<std::string> fields = - absl::StrSplit(line, absl::ByChar(' '), absl::AllowEmpty()); - if (fields.size() < 10 || fields.size() > 11) { - return PosixError( - EINVAL, absl::StrFormat( - "Unexpected number of tokens, got %d, content: <<%s>>", - fields.size(), content)); - } - - ASSIGN_OR_RETURN_ERRNO(entry.id, Atoi<uint64_t>(fields[0])); - ASSIGN_OR_RETURN_ERRNO(entry.parent_id, Atoi<uint64_t>(fields[1])); - - std::vector<std::string> devs = - absl::StrSplit(fields[2], absl::ByChar(':')); - if (devs.size() != 2) { - return PosixError( - EINVAL, - absl::StrFormat( - "Failed to parse dev number field %s: too many tokens, got %d", - fields[2], devs.size())); - } - ASSIGN_OR_RETURN_ERRNO(entry.major, Atoi<dev_t>(devs[0])); - ASSIGN_OR_RETURN_ERRNO(entry.minor, Atoi<dev_t>(devs[1])); - - entry.root = fields[3]; - entry.mount_point = fields[4]; - entry.mount_opts = fields[5]; - - // The optional field (fields[6]) may or may not be present. We know based - // on the total number of tokens. - int off = -1; - if (fields.size() == 11) { - entry.optional = fields[6]; - off = 0; - } - // Field 7 is the optional field terminator char '-'. - entry.fstype = fields[8 + off]; - entry.mount_source = fields[9 + off]; - entry.super_opts = fields[10 + off]; - - entries.push_back(entry); - } - std::cerr << "<end of /proc/self/mountinfo>" << std::endl; - - return entries; -} - -absl::flat_hash_map<std::string, std::string> ParseMountOptions( - std::string mopts) { - absl::flat_hash_map<std::string, std::string> entries; - const std::vector<std::string> tokens = - absl::StrSplit(mopts, absl::ByChar(','), absl::AllowEmpty()); - for (const auto& token : tokens) { - std::vector<std::string> kv = - absl::StrSplit(token, absl::MaxSplits('=', 1)); - if (kv.size() == 2) { - entries[kv[0]] = kv[1]; - } else if (kv.size() == 1) { - entries[kv[0]] = ""; - } else { - TEST_CHECK_MSG( - false, - absl::StrFormat( - "Invalid mount option token '%s', was split into %d subtokens", - token, kv.size()) - .c_str()); - } - } - return entries; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/mount_util.h b/test/util/mount_util.h deleted file mode 100644 index 3f8a1c0f1..000000000 --- a/test/util/mount_util.h +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_MOUNT_UTIL_H_ -#define GVISOR_TEST_UTIL_MOUNT_UTIL_H_ - -#include <errno.h> -#include <sys/mount.h> - -#include <functional> -#include <string> - -#include "gmock/gmock.h" -#include "absl/container/flat_hash_map.h" -#include "test/util/cleanup.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -// Mount mounts the filesystem, and unmounts when the returned reference is -// destroyed. -inline PosixErrorOr<Cleanup> Mount(const std::string& source, - const std::string& target, - const std::string& fstype, - uint64_t mountflags, const std::string& data, - uint64_t umountflags) { - if (mount(source.c_str(), target.c_str(), fstype.c_str(), mountflags, - data.c_str()) == -1) { - return PosixError(errno, "mount failed"); - } - return Cleanup([target, umountflags]() { - EXPECT_THAT(umount2(target.c_str(), umountflags), SyscallSucceeds()); - }); -} - -struct ProcMountsEntry { - std::string spec; - std::string mount_point; - std::string fstype; - std::string mount_opts; - uint32_t dump; - uint32_t fsck; -}; - -// ProcSelfMountsEntries returns a parsed representation of /proc/self/mounts. -PosixErrorOr<std::vector<ProcMountsEntry>> ProcSelfMountsEntries(); - -// ProcSelfMountsEntries returns a parsed representation of mounts from the -// provided content. -PosixErrorOr<std::vector<ProcMountsEntry>> ProcSelfMountsEntriesFrom( - const std::string& content); - -struct ProcMountInfoEntry { - uint64_t id; - uint64_t parent_id; - dev_t major; - dev_t minor; - std::string root; - std::string mount_point; - std::string mount_opts; - std::string optional; - std::string fstype; - std::string mount_source; - std::string super_opts; -}; - -// ProcSelfMountInfoEntries returns a parsed representation of -// /proc/self/mountinfo. -PosixErrorOr<std::vector<ProcMountInfoEntry>> ProcSelfMountInfoEntries(); - -// ProcSelfMountInfoEntriesFrom returns a parsed representation of -// mountinfo from the provided content. -PosixErrorOr<std::vector<ProcMountInfoEntry>> ProcSelfMountInfoEntriesFrom( - const std::string&); - -// Interprets the input string mopts as a comma separated list of mount -// options. A mount option can either be just a value, or a key=value pair. For -// example, the string "rw,relatime,fd=7" will be parsed into a map like { "rw": -// "", "relatime": "", "fd": "7" }. -absl::flat_hash_map<std::string, std::string> ParseMountOptions( - std::string mopts); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_MOUNT_UTIL_H_ diff --git a/test/util/mount_util_test.cc b/test/util/mount_util_test.cc deleted file mode 100644 index 2bcb6cc43..000000000 --- a/test/util/mount_util_test.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/mount_util.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(ParseMounts, Mounts) { - auto entries = ASSERT_NO_ERRNO_AND_VALUE(ProcSelfMountsEntriesFrom( - R"proc(sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0 -proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0 - /mnt tmpfs rw,noexec 0 0 -)proc")); - EXPECT_EQ(entries.size(), 3); -} - -TEST(ParseMounts, MountInfo) { - auto entries = ASSERT_NO_ERRNO_AND_VALUE(ProcSelfMountInfoEntriesFrom( - R"proc(22 28 0:20 / /sys rw,relatime shared:7 - sysfs sysfs rw -23 28 0:21 / /proc rw,relatime shared:14 - proc proc rw -2007 8844 0:278 / /mnt rw,noexec - tmpfs rw,mode=123,uid=268601820,gid=5000 -)proc")); - EXPECT_EQ(entries.size(), 3); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/util/multiprocess_util.cc b/test/util/multiprocess_util.cc deleted file mode 100644 index a6b0de24b..000000000 --- a/test/util/multiprocess_util.cc +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/multiprocess_util.h" - -#include <asm/unistd.h> -#include <errno.h> -#include <fcntl.h> -#include <signal.h> -#include <sys/prctl.h> -#include <unistd.h> - -#include "absl/strings/str_cat.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// exec_fn wraps a variant of the exec family, e.g. execve or execveat. -PosixErrorOr<Cleanup> ForkAndExecHelper(const std::function<void()>& exec_fn, - const std::function<void()>& fn, - pid_t* child, int* execve_errno) { - int pfds[2]; - int ret = pipe2(pfds, O_CLOEXEC); - if (ret < 0) { - return PosixError(errno, "pipe failed"); - } - FileDescriptor rfd(pfds[0]); - FileDescriptor wfd(pfds[1]); - - int parent_stdout = dup(STDOUT_FILENO); - if (parent_stdout < 0) { - return PosixError(errno, "dup stdout"); - } - int parent_stderr = dup(STDERR_FILENO); - if (parent_stdout < 0) { - return PosixError(errno, "dup stderr"); - } - - pid_t pid = fork(); - if (pid < 0) { - return PosixError(errno, "fork failed"); - } else if (pid == 0) { - // Child. - rfd.reset(); - if (dup2(parent_stdout, STDOUT_FILENO) < 0) { - _exit(3); - } - if (dup2(parent_stderr, STDERR_FILENO) < 0) { - _exit(4); - } - close(parent_stdout); - close(parent_stderr); - - // Clean ourself up in case the parent doesn't. - if (prctl(PR_SET_PDEATHSIG, SIGKILL)) { - _exit(3); - } - - if (fn) { - fn(); - } - - // Call variant of exec function. - exec_fn(); - - int error = errno; - if (WriteFd(pfds[1], &error, sizeof(error)) != sizeof(error)) { - // We can't do much if the write fails, but we can at least exit with a - // different code. - _exit(2); - } - _exit(1); - } - - // Parent. - if (child) { - *child = pid; - } - - auto cleanup = Cleanup([pid] { - kill(pid, SIGKILL); - RetryEINTR(waitpid)(pid, nullptr, 0); - }); - - wfd.reset(); - - int read_errno; - ret = ReadFd(rfd.get(), &read_errno, sizeof(read_errno)); - if (ret == 0) { - // Other end of the pipe closed, execve must have succeeded. - read_errno = 0; - } else if (ret < 0) { - return PosixError(errno, "read pipe failed"); - } else if (ret != sizeof(read_errno)) { - return PosixError(EPIPE, absl::StrCat("pipe read wrong size ", ret)); - } - - if (execve_errno) { - *execve_errno = read_errno; - } - - return std::move(cleanup); -} - -} // namespace - -PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename, - const ExecveArray& argv, - const ExecveArray& envv, - const std::function<void()>& fn, pid_t* child, - int* execve_errno) { - char* const* argv_data = argv.get(); - char* const* envv_data = envv.get(); - const std::function<void()> exec_fn = [=] { - execve(filename.c_str(), argv_data, envv_data); - }; - return ForkAndExecHelper(exec_fn, fn, child, execve_errno); -} - -PosixErrorOr<Cleanup> ForkAndExecveat(const int32_t dirfd, - const std::string& pathname, - const ExecveArray& argv, - const ExecveArray& envv, const int flags, - const std::function<void()>& fn, - pid_t* child, int* execve_errno) { - char* const* argv_data = argv.get(); - char* const* envv_data = envv.get(); - const std::function<void()> exec_fn = [=] { - syscall(__NR_execveat, dirfd, pathname.c_str(), argv_data, envv_data, - flags); - }; - return ForkAndExecHelper(exec_fn, fn, child, execve_errno); -} - -PosixErrorOr<int> InForkedProcess(const std::function<void()>& fn) { - pid_t pid = fork(); - if (pid == 0) { - fn(); - TEST_CHECK_MSG(!::testing::Test::HasFailure(), - "EXPECT*/ASSERT* failed. These are not async-signal-safe " - "and must not be called from fn."); - _exit(0); - } - MaybeSave(); - if (pid < 0) { - return PosixError(errno, "fork failed"); - } - - int status; - if (waitpid(pid, &status, 0) < 0) { - return PosixError(errno, "waitpid failed"); - } - - return status; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/multiprocess_util.h b/test/util/multiprocess_util.h deleted file mode 100644 index 840fde4ee..000000000 --- a/test/util/multiprocess_util.h +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_MULTIPROCESS_UTIL_H_ -#define GVISOR_TEST_UTIL_MULTIPROCESS_UTIL_H_ - -#include <unistd.h> - -#include <algorithm> -#include <string> -#include <utility> -#include <vector> - -#include "absl/strings/string_view.h" -#include "test/util/cleanup.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -// Immutable holder for a dynamically-sized array of pointers to mutable char, -// terminated by a null pointer, as required for the argv and envp arguments to -// execve(2). -class ExecveArray { - public: - // Constructs an empty ExecveArray. - ExecveArray() = default; - - // Constructs an ExecveArray by copying strings from the given range. T must - // be a range over ranges of char. - template <typename T> - explicit ExecveArray(T const& strs) : ExecveArray(strs.begin(), strs.end()) {} - - // Constructs an ExecveArray by copying strings from [first, last). InputIt - // must be an input iterator over a range over char. - template <typename InputIt> - ExecveArray(InputIt first, InputIt last) { - std::vector<size_t> offsets; - auto output_it = std::back_inserter(str_); - for (InputIt it = first; it != last; ++it) { - offsets.push_back(str_.size()); - auto const& s = *it; - std::copy(s.begin(), s.end(), output_it); - str_.push_back('\0'); - } - ptrs_.reserve(offsets.size() + 1); - for (auto offset : offsets) { - ptrs_.push_back(str_.data() + offset); - } - ptrs_.push_back(nullptr); - } - - // Constructs an ExecveArray by copying strings from list. This overload must - // exist independently of the single-argument template constructor because - // std::initializer_list does not participate in template argument deduction - // (i.e. cannot be type-inferred in an invocation of the templated - // constructor). - /* implicit */ ExecveArray(std::initializer_list<absl::string_view> list) - : ExecveArray(list.begin(), list.end()) {} - - // Disable move construction and assignment since ptrs_ points into str_. - ExecveArray(ExecveArray&&) = delete; - ExecveArray& operator=(ExecveArray&&) = delete; - - char* const* get() const { return ptrs_.data(); } - size_t get_size() { return str_.size(); } - - private: - std::vector<char> str_; - std::vector<char*> ptrs_; -}; - -// Simplified version of SubProcess. Returns OK and a cleanup function to kill -// the child if it made it to execve. -// -// fn is run between fork and exec. If it needs to fail, it should exit the -// process. -// -// The child pid is returned via child, if provided. -// execve's error code is returned via execve_errno, if provided. -PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename, - const ExecveArray& argv, - const ExecveArray& envv, - const std::function<void()>& fn, pid_t* child, - int* execve_errno); - -inline PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename, - const ExecveArray& argv, - const ExecveArray& envv, pid_t* child, - int* execve_errno) { - return ForkAndExec( - filename, argv, envv, [] {}, child, execve_errno); -} - -// Equivalent to ForkAndExec, except using dirfd and flags with execveat. -PosixErrorOr<Cleanup> ForkAndExecveat(int32_t dirfd, - const std::string& pathname, - const ExecveArray& argv, - const ExecveArray& envv, int flags, - const std::function<void()>& fn, - pid_t* child, int* execve_errno); - -inline PosixErrorOr<Cleanup> ForkAndExecveat(int32_t dirfd, - const std::string& pathname, - const ExecveArray& argv, - const ExecveArray& envv, int flags, - pid_t* child, int* execve_errno) { - return ForkAndExecveat( - dirfd, pathname, argv, envv, flags, [] {}, child, execve_errno); -} - -// Calls fn in a forked subprocess and returns the exit status of the -// subprocess. -// -// fn must be async-signal-safe. Use of ASSERT/EXPECT functions is prohibited. -// Use TEST_CHECK variants instead. -PosixErrorOr<int> InForkedProcess(const std::function<void()>& fn); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_MULTIPROCESS_UTIL_H_ diff --git a/test/util/platform_util.cc b/test/util/platform_util.cc deleted file mode 100644 index c9200d381..000000000 --- a/test/util/platform_util.cc +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2020 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/platform_util.h" - -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -PlatformSupport PlatformSupport32Bit() { - if (GvisorPlatform() == Platform::kPtrace || - GvisorPlatform() == Platform::kKVM) { - return PlatformSupport::NotSupported; - } else { - return PlatformSupport::Allowed; - } -} - -PlatformSupport PlatformSupportAlignmentCheck() { - return PlatformSupport::Allowed; -} - -PlatformSupport PlatformSupportMultiProcess() { - return PlatformSupport::Allowed; -} - -PlatformSupport PlatformSupportInt3() { - if (GvisorPlatform() == Platform::kKVM) { - return PlatformSupport::NotSupported; - } else { - return PlatformSupport::Allowed; - } -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/platform_util.h b/test/util/platform_util.h deleted file mode 100644 index 28cc92371..000000000 --- a/test/util/platform_util.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2020 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_PLATFORM_UTIL_H_ -#define GVISOR_TEST_UTIL_PLATFORM_UTIL_H_ - -namespace gvisor { -namespace testing { - -// PlatformSupport is a generic enumeration of classes of support. -// -// It is up to the individual functions and callers to agree on the precise -// definition for each case. The document here generally refers to 32-bit -// as an example. Many cases will use only NotSupported and Allowed. -enum class PlatformSupport { - // The feature is not supported on the current platform. - // - // In the case of 32-bit, this means that calls will generally be interpreted - // as 64-bit calls, and there is no support for 32-bit binaries, long calls, - // etc. This usually means that the underlying implementation just pretends - // that 32-bit doesn't exist. - NotSupported, - - // Calls will be ignored by the kernel with a fixed error. - Ignored, - - // Calls will result in a SIGSEGV or similar fault. - Segfault, - - // The feature is supported as expected. - // - // In the case of 32-bit, this means that the system call or far call will be - // handled properly. - Allowed, -}; - -PlatformSupport PlatformSupport32Bit(); -PlatformSupport PlatformSupportAlignmentCheck(); -PlatformSupport PlatformSupportMultiProcess(); -PlatformSupport PlatformSupportInt3(); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_PLATFORM_UTL_H_ diff --git a/test/util/posix_error.cc b/test/util/posix_error.cc deleted file mode 100644 index 8522e4c81..000000000 --- a/test/util/posix_error.cc +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/posix_error.h" - -#include <cassert> -#include <cerrno> -#include <cstring> -#include <string> - -#include "absl/strings/str_cat.h" - -namespace gvisor { -namespace testing { - -std::string PosixError::ToString() const { - if (ok()) { - return "No Error"; - } - - std::string ret; - - char strerrno_buf[1024] = {}; - - auto res = strerror_r(errno_, strerrno_buf, sizeof(strerrno_buf)); - -// The GNU version of strerror_r always returns a non-null char* pointing to a -// buffer containing the stringified errno; the XSI version returns a positive -// errno which indicates the result of writing the stringified errno into the -// supplied buffer. The gymnastics below are needed to support both. -#ifndef _GNU_SOURCE - if (res != 0) { - ret = absl::StrCat("PosixError(errno=", errno_, " strerror_r FAILED(", ret, - "))"); - } else { - ret = absl::StrCat("PosixError(errno=", errno_, " ", strerrno_buf, ")"); - } -#else - ret = absl::StrCat("PosixError(errno=", errno_, " ", res, ")"); -#endif - - if (strnlen(msg_, sizeof(msg_)) > 0) { - ret.append(" "); - ret.append(msg_); - } - - return ret; -} - -::std::ostream& operator<<(::std::ostream& os, const PosixError& e) { - os << e.ToString(); - return os; -} - -void PosixErrorIsMatcherCommonImpl::DescribeTo(std::ostream* os) const { - *os << "has an errno value that "; - code_matcher_.DescribeTo(os); - *os << ", and has an error message that "; - message_matcher_.DescribeTo(os); -} - -void PosixErrorIsMatcherCommonImpl::DescribeNegationTo(std::ostream* os) const { - *os << "has an errno value that "; - code_matcher_.DescribeNegationTo(os); - *os << ", or has an error message that "; - message_matcher_.DescribeNegationTo(os); -} - -bool PosixErrorIsMatcherCommonImpl::MatchAndExplain( - const PosixError& error, - ::testing::MatchResultListener* result_listener) const { - ::testing::StringMatchResultListener inner_listener; - - inner_listener.Clear(); - if (!code_matcher_.MatchAndExplain(error.errno_value(), &inner_listener)) { - return false; - } - - if (!message_matcher_.Matches(error.message())) { - return false; - } - - return true; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/posix_error.h b/test/util/posix_error.h deleted file mode 100644 index 9ca09b77c..000000000 --- a/test/util/posix_error.h +++ /dev/null @@ -1,451 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_POSIX_ERROR_H_ -#define GVISOR_TEST_UTIL_POSIX_ERROR_H_ - -#include <string> - -#include "gmock/gmock.h" -#include "absl/base/attributes.h" -#include "absl/strings/string_view.h" -#include "absl/types/variant.h" -#include "test/util/logging.h" - -namespace gvisor { -namespace testing { - -// PosixError must be async-signal-safe. -class ABSL_MUST_USE_RESULT PosixError { - public: - PosixError() {} - - explicit PosixError(int errno_value) : errno_(errno_value) {} - - PosixError(int errno_value, std::string_view msg) : errno_(errno_value) { - // Check that `msg` will fit, leaving room for '\0' at the end. - TEST_CHECK(msg.size() < sizeof(msg_)); - msg.copy(msg_, msg.size()); - } - - PosixError(PosixError&& other) = default; - PosixError& operator=(PosixError&& other) = default; - PosixError(const PosixError&) = default; - PosixError& operator=(const PosixError&) = default; - - bool ok() const { return errno_ == 0; } - - // Returns a reference to *this to make matchers compatible with - // PosixErrorOr. - const PosixError& error() const { return *this; } - - int errno_value() const { return errno_; } - const char* message() const { return msg_; } - - // ToString produces a full string representation of this posix error - // including the printable representation of the errno and the error message. - std::string ToString() const; - - // Ignores any errors. This method does nothing except potentially suppress - // complaints from any tools that are checking that errors are not dropped on - // the floor. - void IgnoreError() const {} - - private: - int errno_ = 0; - char msg_[1024] = {}; -}; - -template <typename T> -class ABSL_MUST_USE_RESULT PosixErrorOr { - public: - // A PosixErrorOr will check fail if it is constructed with NoError(). - PosixErrorOr(const PosixError& error); - PosixErrorOr(const T& value); - PosixErrorOr(T&& value); - - PosixErrorOr(PosixErrorOr&& other) = default; - PosixErrorOr& operator=(PosixErrorOr&& other) = default; - PosixErrorOr(const PosixErrorOr&) = default; - PosixErrorOr& operator=(const PosixErrorOr&) = default; - - // Conversion copy/move constructor, T must be convertible from U. - template <typename U> - friend class PosixErrorOr; - - template <typename U> - PosixErrorOr(PosixErrorOr<U> other); - - template <typename U> - PosixErrorOr& operator=(PosixErrorOr<U> other); - - // Returns true if this PosixErrorOr contains some T. - bool ok() const; - - // Return a copy of the contained PosixError or NoError(). - PosixError error() const; - - // Returns a reference to our current value, or CHECK-fails if !this->ok(). - const T& ValueOrDie() const&; - T& ValueOrDie() &; - const T&& ValueOrDie() const&&; - T&& ValueOrDie() &&; - - // Ignores any errors. This method does nothing except potentially suppress - // complaints from any tools that are checking that errors are not dropped on - // the floor. - void IgnoreError() const {} - - private: - absl::variant<T, PosixError> value_; - - friend class PosixErrorIsMatcherCommonImpl; -}; - -template <typename T> -PosixErrorOr<T>::PosixErrorOr(const PosixError& error) : value_(error) { - TEST_CHECK_MSG( - !error.ok(), - "Constructing PosixErrorOr with NoError, eg. errno 0 is not allowed."); -} - -template <typename T> -PosixErrorOr<T>::PosixErrorOr(const T& value) : value_(value) {} - -template <typename T> -PosixErrorOr<T>::PosixErrorOr(T&& value) : value_(std::move(value)) {} - -// Conversion copy/move constructor, T must be convertible from U. -template <typename T> -template <typename U> -inline PosixErrorOr<T>::PosixErrorOr(PosixErrorOr<U> other) { - if (absl::holds_alternative<U>(other.value_)) { - // T is convertible from U. - value_ = absl::get<U>(std::move(other.value_)); - } else if (absl::holds_alternative<PosixError>(other.value_)) { - value_ = absl::get<PosixError>(std::move(other.value_)); - } else { - TEST_CHECK_MSG(false, "PosixErrorOr does not contain PosixError or value"); - } -} - -template <typename T> -template <typename U> -inline PosixErrorOr<T>& PosixErrorOr<T>::operator=(PosixErrorOr<U> other) { - if (absl::holds_alternative<U>(other.value_)) { - // T is convertible from U. - value_ = absl::get<U>(std::move(other.value_)); - } else if (absl::holds_alternative<PosixError>(other.value_)) { - value_ = absl::get<PosixError>(std::move(other.value_)); - } else { - TEST_CHECK_MSG(false, "PosixErrorOr does not contain PosixError or value"); - } - return *this; -} - -template <typename T> -PosixError PosixErrorOr<T>::error() const { - if (!absl::holds_alternative<PosixError>(value_)) { - return PosixError(); - } - return absl::get<PosixError>(value_); -} - -template <typename T> -bool PosixErrorOr<T>::ok() const { - return absl::holds_alternative<T>(value_); -} - -template <typename T> -const T& PosixErrorOr<T>::ValueOrDie() const& { - TEST_CHECK(absl::holds_alternative<T>(value_)); - return absl::get<T>(value_); -} - -template <typename T> -T& PosixErrorOr<T>::ValueOrDie() & { - TEST_CHECK(absl::holds_alternative<T>(value_)); - return absl::get<T>(value_); -} - -template <typename T> -const T&& PosixErrorOr<T>::ValueOrDie() const&& { - TEST_CHECK(absl::holds_alternative<T>(value_)); - return std::move(absl::get<T>(value_)); -} - -template <typename T> -T&& PosixErrorOr<T>::ValueOrDie() && { - TEST_CHECK(absl::holds_alternative<T>(value_)); - return std::move(absl::get<T>(value_)); -} - -extern ::std::ostream& operator<<(::std::ostream& os, const PosixError& e); - -template <typename T> -::std::ostream& operator<<(::std::ostream& os, const PosixErrorOr<T>& e) { - os << e.error(); - return os; -} - -// NoError is a PosixError that represents a successful state, i.e. No Error. -inline PosixError NoError() { return PosixError(); } - -// Monomorphic implementation of matcher IsPosixErrorOk() for a given type T. -// T can be PosixError, PosixErrorOr<>, or a reference to either of them. -template <typename T> -class MonoPosixErrorIsOkMatcherImpl : public ::testing::MatcherInterface<T> { - public: - void DescribeTo(std::ostream* os) const override { *os << "is OK"; } - void DescribeNegationTo(std::ostream* os) const override { - *os << "is not OK"; - } - bool MatchAndExplain(T actual_value, - ::testing::MatchResultListener*) const override { - return actual_value.ok(); - } -}; - -// Implements IsPosixErrorOkMatcher() as a polymorphic matcher. -class IsPosixErrorOkMatcher { - public: - template <typename T> - operator ::testing::Matcher<T>() const { // NOLINT - return MakeMatcher(new MonoPosixErrorIsOkMatcherImpl<T>()); - } -}; - -// Monomorphic implementation of a matcher for a PosixErrorOr. -template <typename PosixErrorOrType> -class IsPosixErrorOkAndHoldsMatcherImpl - : public ::testing::MatcherInterface<PosixErrorOrType> { - public: - using ValueType = typename std::remove_reference<decltype( - std::declval<PosixErrorOrType>().ValueOrDie())>::type; - - template <typename InnerMatcher> - explicit IsPosixErrorOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher) - : inner_matcher_(::testing::SafeMatcherCast<const ValueType&>( - std::forward<InnerMatcher>(inner_matcher))) {} - - void DescribeTo(std::ostream* os) const override { - *os << "is OK and has a value that "; - inner_matcher_.DescribeTo(os); - } - - void DescribeNegationTo(std::ostream* os) const override { - *os << "isn't OK or has a value that "; - inner_matcher_.DescribeNegationTo(os); - } - - bool MatchAndExplain( - PosixErrorOrType actual_value, - ::testing::MatchResultListener* listener) const override { - // We can't extract the value if it doesn't contain one. - if (!actual_value.ok()) { - return false; - } - - ::testing::StringMatchResultListener inner_listener; - const bool matches = inner_matcher_.MatchAndExplain( - actual_value.ValueOrDie(), &inner_listener); - const std::string inner_explanation = inner_listener.str(); - *listener << "has a value " - << ::testing::PrintToString(actual_value.ValueOrDie()); - - if (!inner_explanation.empty()) { - *listener << " " << inner_explanation; - } - return matches; - } - - private: - const ::testing::Matcher<const ValueType&> inner_matcher_; -}; - -// Implements IsOkAndHolds() as a polymorphic matcher. -template <typename InnerMatcher> -class IsPosixErrorOkAndHoldsMatcher { - public: - explicit IsPosixErrorOkAndHoldsMatcher(InnerMatcher inner_matcher) - : inner_matcher_(std::move(inner_matcher)) {} - - // Converts this polymorphic matcher to a monomorphic one of the given type. - // PosixErrorOrType can be either PosixErrorOr<T> or a reference to - // PosixErrorOr<T>. - template <typename PosixErrorOrType> - operator ::testing::Matcher<PosixErrorOrType>() const { // NOLINT - return ::testing::MakeMatcher( - new IsPosixErrorOkAndHoldsMatcherImpl<PosixErrorOrType>( - inner_matcher_)); - } - - private: - const InnerMatcher inner_matcher_; -}; - -// PosixErrorIs() is a polymorphic matcher. This class is the common -// implementation of it shared by all types T where PosixErrorIs() can be -// used as a Matcher<T>. -class PosixErrorIsMatcherCommonImpl { - public: - PosixErrorIsMatcherCommonImpl( - ::testing::Matcher<int> code_matcher, - ::testing::Matcher<const std::string&> message_matcher) - : code_matcher_(std::move(code_matcher)), - message_matcher_(std::move(message_matcher)) {} - - void DescribeTo(std::ostream* os) const; - - void DescribeNegationTo(std::ostream* os) const; - - bool MatchAndExplain(const PosixError& error, - ::testing::MatchResultListener* result_listener) const; - - template <typename T> - bool MatchAndExplain(const PosixErrorOr<T>& error_or, - ::testing::MatchResultListener* result_listener) const { - if (error_or.ok()) { - *result_listener << "has a value " - << ::testing::PrintToString(error_or.ValueOrDie()); - return false; - } - - return MatchAndExplain(error_or.error(), result_listener); - } - - private: - const ::testing::Matcher<int> code_matcher_; - const ::testing::Matcher<const std::string&> message_matcher_; -}; - -// Monomorphic implementation of matcher PosixErrorIs() for a given type -// T. T can be PosixError, PosixErrorOr<>, or a reference to either of them. -template <typename T> -class MonoPosixErrorIsMatcherImpl : public ::testing::MatcherInterface<T> { - public: - explicit MonoPosixErrorIsMatcherImpl( - PosixErrorIsMatcherCommonImpl common_impl) - : common_impl_(std::move(common_impl)) {} - - void DescribeTo(std::ostream* os) const override { - common_impl_.DescribeTo(os); - } - - void DescribeNegationTo(std::ostream* os) const override { - common_impl_.DescribeNegationTo(os); - } - - bool MatchAndExplain( - T actual_value, - ::testing::MatchResultListener* result_listener) const override { - return common_impl_.MatchAndExplain(actual_value, result_listener); - } - - private: - PosixErrorIsMatcherCommonImpl common_impl_; -}; - -inline ::testing::Matcher<int> ToErrorCodeMatcher( - const ::testing::Matcher<int>& m) { - return m; -} - -// Implements PosixErrorIs() as a polymorphic matcher. -class PosixErrorIsMatcher { - public: - template <typename ErrorCodeMatcher> - PosixErrorIsMatcher(ErrorCodeMatcher&& code_matcher, - ::testing::Matcher<const std::string&> message_matcher) - : common_impl_( - ToErrorCodeMatcher(std::forward<ErrorCodeMatcher>(code_matcher)), - std::move(message_matcher)) {} - - // Converts this polymorphic matcher to a monomorphic matcher of the - // given type. T can be StatusOr<>, Status, or a reference to - // either of them. - template <typename T> - operator ::testing::Matcher<T>() const { // NOLINT - return MakeMatcher(new MonoPosixErrorIsMatcherImpl<T>(common_impl_)); - } - - private: - const PosixErrorIsMatcherCommonImpl common_impl_; -}; - -// Returns a gMock matcher that matches a PosixError or PosixErrorOr<> whose -// whose error code matches code_matcher, and whose error message matches -// message_matcher. -template <typename ErrorCodeMatcher> -PosixErrorIsMatcher PosixErrorIs( - ErrorCodeMatcher&& code_matcher, - ::testing::Matcher<const std::string&> message_matcher) { - return PosixErrorIsMatcher(std::forward<ErrorCodeMatcher>(code_matcher), - std::move(message_matcher)); -} - -// Returns a gMock matcher that matches a PosixErrorOr<> which is ok() and -// value matches the inner matcher. -template <typename InnerMatcher> -IsPosixErrorOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type> -IsPosixErrorOkAndHolds(InnerMatcher&& inner_matcher) { - return IsPosixErrorOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type>( - std::forward<InnerMatcher>(inner_matcher)); -} - -// Internal helper for concatenating macro values. -#define POSIX_ERROR_IMPL_CONCAT_INNER_(x, y) x##y -#define POSIX_ERROR_IMPL_CONCAT_(x, y) POSIX_ERROR_IMPL_CONCAT_INNER_(x, y) - -#define POSIX_ERROR_IMPL_ASSIGN_OR_RETURN_(posixerroror, lhs, rexpr) \ - auto posixerroror = (rexpr); \ - if (!posixerroror.ok()) { \ - return (posixerroror.error()); \ - } \ - lhs = std::move(posixerroror).ValueOrDie() - -#define EXPECT_NO_ERRNO(expression) \ - EXPECT_THAT(expression, IsPosixErrorOkMatcher()) -#define ASSERT_NO_ERRNO(expression) \ - ASSERT_THAT(expression, IsPosixErrorOkMatcher()) - -#define ASSIGN_OR_RETURN_ERRNO(lhs, rexpr) \ - POSIX_ERROR_IMPL_ASSIGN_OR_RETURN_( \ - POSIX_ERROR_IMPL_CONCAT_(_status_or_value, __LINE__), lhs, rexpr) - -#define RETURN_IF_ERRNO(s) \ - do { \ - if (!s.ok()) { \ - return s; \ - } \ - } while (false); - -#define ASSERT_NO_ERRNO_AND_VALUE(expr) \ - ({ \ - auto _expr_result = (expr); \ - ASSERT_NO_ERRNO(_expr_result); \ - std::move(_expr_result).ValueOrDie(); \ - }) - -#define EXPECT_NO_ERRNO_AND_VALUE(expr) \ - ({ \ - auto _expr_result = (expr); \ - EXPECT_NO_ERRNO(_expr_result); \ - std::move(_expr_result).ValueOrDie(); \ - }) - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_POSIX_ERROR_H_ diff --git a/test/util/posix_error_test.cc b/test/util/posix_error_test.cc deleted file mode 100644 index bf9465abb..000000000 --- a/test/util/posix_error_test.cc +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/posix_error.h" - -#include <errno.h> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(PosixErrorTest, PosixError) { - auto err = PosixError(EAGAIN); - EXPECT_THAT(err, PosixErrorIs(EAGAIN, "")); -} - -TEST(PosixErrorTest, PosixErrorOrPosixError) { - auto err = PosixErrorOr<std::nullptr_t>(PosixError(EAGAIN)); - EXPECT_THAT(err, PosixErrorIs(EAGAIN, "")); -} - -TEST(PosixErrorTest, PosixErrorOrNullptr) { - auto err = PosixErrorOr<std::nullptr_t>(nullptr); - EXPECT_TRUE(err.ok()); - EXPECT_NO_ERRNO(err); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/util/proc_util.cc b/test/util/proc_util.cc deleted file mode 100644 index 34d636ba9..000000000 --- a/test/util/proc_util.cc +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/proc_util.h" - -#include <algorithm> -#include <iostream> -#include <vector> - -#include "absl/strings/ascii.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_split.h" -#include "absl/strings/string_view.h" -#include "test/util/fs_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -// Parses a single line from /proc/<xxx>/maps. -PosixErrorOr<ProcMapsEntry> ParseProcMapsLine(absl::string_view line) { - ProcMapsEntry map_entry = {}; - - // Limit splitting to 6 parts so that if there is a file path and it contains - // spaces, the file path is not split. - std::vector<std::string> parts = - absl::StrSplit(line, absl::MaxSplits(' ', 5), absl::SkipEmpty()); - - // parts.size() should be 6 if there is a file name specified, and 5 - // otherwise. - if (parts.size() < 5) { - return PosixError(EINVAL, absl::StrCat("Invalid line: ", line)); - } - - // Address range in the form X-X where X are hex values without leading 0x. - std::vector<std::string> addresses = absl::StrSplit(parts[0], '-'); - if (addresses.size() != 2) { - return PosixError(EINVAL, - absl::StrCat("Invalid address range: ", parts[0])); - } - ASSIGN_OR_RETURN_ERRNO(map_entry.start, AtoiBase(addresses[0], 16)); - ASSIGN_OR_RETURN_ERRNO(map_entry.end, AtoiBase(addresses[1], 16)); - - // Permissions are four bytes of the form rwxp or - if permission not set. - if (parts[1].size() != 4) { - return PosixError(EINVAL, - absl::StrCat("Invalid permission field: ", parts[1])); - } - - map_entry.readable = parts[1][0] == 'r'; - map_entry.writable = parts[1][1] == 'w'; - map_entry.executable = parts[1][2] == 'x'; - map_entry.priv = parts[1][3] == 'p'; - - ASSIGN_OR_RETURN_ERRNO(map_entry.offset, AtoiBase(parts[2], 16)); - - std::vector<std::string> device = absl::StrSplit(parts[3], ':'); - if (device.size() != 2) { - return PosixError(EINVAL, absl::StrCat("Invalid device: ", parts[3])); - } - ASSIGN_OR_RETURN_ERRNO(map_entry.major, AtoiBase(device[0], 16)); - ASSIGN_OR_RETURN_ERRNO(map_entry.minor, AtoiBase(device[1], 16)); - - ASSIGN_OR_RETURN_ERRNO(map_entry.inode, Atoi<int64_t>(parts[4])); - if (parts.size() == 6) { - // A filename is present. However, absl::StrSplit retained the whitespace - // between the inode number and the filename. - map_entry.filename = - std::string(absl::StripLeadingAsciiWhitespace(parts[5])); - } - - return map_entry; -} - -PosixErrorOr<std::vector<ProcMapsEntry>> ParseProcMaps( - absl::string_view contents) { - std::vector<ProcMapsEntry> entries; - auto lines = absl::StrSplit(contents, '\n', absl::SkipEmpty()); - for (const auto& l : lines) { - std::cout << "line: " << l << std::endl; - ASSIGN_OR_RETURN_ERRNO(auto entry, ParseProcMapsLine(l)); - entries.push_back(entry); - } - return entries; -} - -PosixErrorOr<bool> IsVsyscallEnabled() { - ASSIGN_OR_RETURN_ERRNO(auto contents, GetContents("/proc/self/maps")); - ASSIGN_OR_RETURN_ERRNO(auto maps, ParseProcMaps(contents)); - return std::any_of(maps.begin(), maps.end(), [](const ProcMapsEntry& e) { - return e.filename == "[vsyscall]"; - }); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/proc_util.h b/test/util/proc_util.h deleted file mode 100644 index af209a51e..000000000 --- a/test/util/proc_util.h +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_PROC_UTIL_H_ -#define GVISOR_TEST_UTIL_PROC_UTIL_H_ - -#include <ostream> -#include <string> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -// ProcMapsEntry contains the data from a single line in /proc/<xxx>/maps. -struct ProcMapsEntry { - uint64_t start; - uint64_t end; - bool readable; - bool writable; - bool executable; - bool priv; - uint64_t offset; - int major; - int minor; - int64_t inode; - std::string filename; -}; - -// Parses a ProcMaps line or returns an error. -PosixErrorOr<ProcMapsEntry> ParseProcMapsLine(absl::string_view line); -PosixErrorOr<std::vector<ProcMapsEntry>> ParseProcMaps( - absl::string_view contents); - -// Returns true if vsyscall (emmulation or not) is enabled. -PosixErrorOr<bool> IsVsyscallEnabled(); - -// Printer for ProcMapsEntry. -inline std::ostream& operator<<(std::ostream& os, const ProcMapsEntry& entry) { - std::string str = - absl::StrCat(absl::Hex(entry.start, absl::PadSpec::kZeroPad8), "-", - absl::Hex(entry.end, absl::PadSpec::kZeroPad8), " "); - - absl::StrAppend(&str, entry.readable ? "r" : "-"); - absl::StrAppend(&str, entry.writable ? "w" : "-"); - absl::StrAppend(&str, entry.executable ? "x" : "-"); - absl::StrAppend(&str, entry.priv ? "p" : "s"); - - absl::StrAppend(&str, " ", absl::Hex(entry.offset, absl::PadSpec::kZeroPad8), - " ", absl::Hex(entry.major, absl::PadSpec::kZeroPad2), ":", - absl::Hex(entry.minor, absl::PadSpec::kZeroPad2), " ", - entry.inode); - if (absl::string_view(entry.filename) != "") { - // Pad to column 74 - int pad = 73 - str.length(); - if (pad > 0) { - absl::StrAppend(&str, std::string(pad, ' ')); - } - absl::StrAppend(&str, entry.filename); - } - os << str; - return os; -} - -// Printer for std::vector<ProcMapsEntry>. -inline std::ostream& operator<<(std::ostream& os, - const std::vector<ProcMapsEntry>& vec) { - for (unsigned int i = 0; i < vec.size(); i++) { - os << vec[i]; - if (i != vec.size() - 1) { - os << "\n"; - } - } - return os; -} - -// GMock printer for std::vector<ProcMapsEntry>. -inline void PrintTo(const std::vector<ProcMapsEntry>& vec, std::ostream* os) { - *os << vec; -} - -// Checks that /proc/pid/maps contains all of the passed mappings. -// -// The major, minor, and inode fields are ignored. -MATCHER_P(ContainsMappings, mappings, - "contains mappings:\n" + ::testing::PrintToString(mappings)) { - auto contents_or = GetContents(absl::StrCat("/proc/", arg, "/maps")); - if (!contents_or.ok()) { - *result_listener << "Unable to read mappings: " - << contents_or.error().ToString(); - return false; - } - - auto maps_or = ParseProcMaps(contents_or.ValueOrDie()); - if (!maps_or.ok()) { - *result_listener << "Unable to parse mappings: " - << maps_or.error().ToString(); - return false; - } - - auto maps = std::move(maps_or).ValueOrDie(); - - // Does maps contain all elements in mappings? The comparator ignores - // the major, minor, and inode fields. - bool all_present = true; - std::for_each(mappings.begin(), mappings.end(), [&](const ProcMapsEntry& e1) { - auto it = - std::find_if(maps.begin(), maps.end(), [&e1](const ProcMapsEntry& e2) { - return e1.start == e2.start && e1.end == e2.end && - e1.readable == e2.readable && e1.writable == e2.writable && - e1.executable == e2.executable && e1.priv == e2.priv && - e1.offset == e2.offset && e1.filename == e2.filename; - }); - if (it == maps.end()) { - // It wasn't found. - if (all_present) { - // We will output the message once and then a line for each mapping - // that wasn't found. - all_present = false; - *result_listener << "Got mappings:\n" - << maps << "\nThat were missing:\n"; - } - *result_listener << e1 << "\n"; - } - }); - - return all_present; -} - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_PROC_UTIL_H_ diff --git a/test/util/proc_util_test.cc b/test/util/proc_util_test.cc deleted file mode 100644 index 71dd2355e..000000000 --- a/test/util/proc_util_test.cc +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/proc_util.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/util/test_util.h" - -using ::testing::IsEmpty; - -namespace gvisor { -namespace testing { - -namespace { - -TEST(ParseProcMapsLineTest, WithoutFilename) { - auto entry = ASSERT_NO_ERRNO_AND_VALUE( - ParseProcMapsLine("2ab4f00b7000-2ab4f00b9000 r-xp 00000000 00:00 0 ")); - EXPECT_EQ(entry.start, 0x2ab4f00b7000); - EXPECT_EQ(entry.end, 0x2ab4f00b9000); - EXPECT_TRUE(entry.readable); - EXPECT_FALSE(entry.writable); - EXPECT_TRUE(entry.executable); - EXPECT_TRUE(entry.priv); - EXPECT_EQ(entry.offset, 0); - EXPECT_EQ(entry.major, 0); - EXPECT_EQ(entry.minor, 0); - EXPECT_EQ(entry.inode, 0); - EXPECT_THAT(entry.filename, IsEmpty()); -} - -TEST(ParseProcMapsLineTest, WithFilename) { - auto entry = ASSERT_NO_ERRNO_AND_VALUE( - ParseProcMapsLine("00407000-00408000 rw-p 00006000 00:0e 10 " - " /bin/cat")); - EXPECT_EQ(entry.start, 0x407000); - EXPECT_EQ(entry.end, 0x408000); - EXPECT_TRUE(entry.readable); - EXPECT_TRUE(entry.writable); - EXPECT_FALSE(entry.executable); - EXPECT_TRUE(entry.priv); - EXPECT_EQ(entry.offset, 0x6000); - EXPECT_EQ(entry.major, 0); - EXPECT_EQ(entry.minor, 0x0e); - EXPECT_EQ(entry.inode, 10); - EXPECT_EQ(entry.filename, "/bin/cat"); -} - -TEST(ParseProcMapsLineTest, WithFilenameContainingSpaces) { - auto entry = ASSERT_NO_ERRNO_AND_VALUE( - ParseProcMapsLine("7f26b3b12000-7f26b3b13000 rw-s 00000000 00:05 1432484 " - " /dev/zero (deleted)")); - EXPECT_EQ(entry.start, 0x7f26b3b12000); - EXPECT_EQ(entry.end, 0x7f26b3b13000); - EXPECT_TRUE(entry.readable); - EXPECT_TRUE(entry.writable); - EXPECT_FALSE(entry.executable); - EXPECT_FALSE(entry.priv); - EXPECT_EQ(entry.offset, 0); - EXPECT_EQ(entry.major, 0); - EXPECT_EQ(entry.minor, 0x05); - EXPECT_EQ(entry.inode, 1432484); - EXPECT_EQ(entry.filename, "/dev/zero (deleted)"); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/util/pty_util.cc b/test/util/pty_util.cc deleted file mode 100644 index 351f4730c..000000000 --- a/test/util/pty_util.cc +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/pty_util.h" - -#include <sys/ioctl.h> -#include <termios.h> - -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr<FileDescriptor> OpenReplica(const FileDescriptor& master) { - return OpenReplica(master, O_NONBLOCK | O_RDWR | O_NOCTTY); -} - -PosixErrorOr<FileDescriptor> OpenReplica(const FileDescriptor& master, - int flags) { - PosixErrorOr<int> n = ReplicaID(master); - if (!n.ok()) { - return PosixErrorOr<FileDescriptor>(n.error()); - } - return Open(absl::StrCat("/dev/pts/", n.ValueOrDie()), flags); -} - -PosixErrorOr<int> ReplicaID(const FileDescriptor& master) { - // Get pty index. - int n; - int ret = ioctl(master.get(), TIOCGPTN, &n); - if (ret < 0) { - return PosixError(errno, "ioctl(TIOCGPTN) failed"); - } - - // Unlock pts. - int unlock = 0; - ret = ioctl(master.get(), TIOCSPTLCK, &unlock); - if (ret < 0) { - return PosixError(errno, "ioctl(TIOSPTLCK) failed"); - } - - return n; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/pty_util.h b/test/util/pty_util.h deleted file mode 100644 index 0cca2182c..000000000 --- a/test/util/pty_util.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_PTY_UTIL_H_ -#define GVISOR_TEST_UTIL_PTY_UTIL_H_ - -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -// Opens the replica end of the passed master as R/W and nonblocking. It does -// not set the replica as the controlling TTY. -PosixErrorOr<FileDescriptor> OpenReplica(const FileDescriptor& master); - -// Identical to the above OpenReplica, but flags are all specified by the -// caller. -PosixErrorOr<FileDescriptor> OpenReplica(const FileDescriptor& master, - int flags); - -// Get the number of the replica end of the master. -PosixErrorOr<int> ReplicaID(const FileDescriptor& master); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_PTY_UTIL_H_ diff --git a/test/util/rlimit_util.cc b/test/util/rlimit_util.cc deleted file mode 100644 index d7bfc1606..000000000 --- a/test/util/rlimit_util.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/rlimit_util.h" - -#include <sys/resource.h> - -#include <cerrno> - -#include "test/util/cleanup.h" -#include "test/util/logging.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr<Cleanup> ScopedSetSoftRlimit(int resource, rlim_t newval) { - struct rlimit old_rlim; - if (getrlimit(resource, &old_rlim) != 0) { - return PosixError(errno, "getrlimit failed"); - } - struct rlimit new_rlim = old_rlim; - new_rlim.rlim_cur = newval; - if (setrlimit(resource, &new_rlim) != 0) { - return PosixError(errno, "setrlimit failed"); - } - return Cleanup([resource, old_rlim] { - TEST_PCHECK(setrlimit(resource, &old_rlim) == 0); - }); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/rlimit_util.h b/test/util/rlimit_util.h deleted file mode 100644 index 873252a32..000000000 --- a/test/util/rlimit_util.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_RLIMIT_UTIL_H_ -#define GVISOR_TEST_UTIL_RLIMIT_UTIL_H_ - -#include <sys/resource.h> -#include <sys/time.h> - -#include "test/util/cleanup.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr<Cleanup> ScopedSetSoftRlimit(int resource, rlim_t newval); - -} // namespace testing -} // namespace gvisor -#endif // GVISOR_TEST_UTIL_RLIMIT_UTIL_H_ diff --git a/test/util/save_util.cc b/test/util/save_util.cc deleted file mode 100644 index 3e724d99b..000000000 --- a/test/util/save_util.cc +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/save_util.h" - -#include <stddef.h> -#include <stdlib.h> -#include <unistd.h> - -#include <atomic> -#include <cerrno> - -#include "absl/types/optional.h" - -namespace gvisor { -namespace testing { -namespace { - -std::atomic<absl::optional<bool>> save_present; - -bool SavePresent() { - auto present = save_present.load(); - if (!present.has_value()) { - present = getenv("GVISOR_SAVE_TEST") != nullptr; - save_present.store(present); - } - return present.value(); -} - -std::atomic<int> save_disable; - -} // namespace - -bool IsRunningWithSaveRestore() { return SavePresent(); } - -void MaybeSave() { - if (SavePresent() && save_disable.load() == 0) { - internal::DoCooperativeSave(); - } -} - -DisableSave::DisableSave() { save_disable++; } - -DisableSave::~DisableSave() { reset(); } - -void DisableSave::reset() { - if (!reset_) { - reset_ = true; - save_disable--; - } -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/save_util.h b/test/util/save_util.h deleted file mode 100644 index e7218ae88..000000000 --- a/test/util/save_util.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_SAVE_UTIL_H_ -#define GVISOR_TEST_UTIL_SAVE_UTIL_H_ - -namespace gvisor { -namespace testing { - -// Returns true if the environment in which the calling process is executing -// allows the test to be checkpointed and restored during execution. -bool IsRunningWithSaveRestore(); - -// May perform a co-operative save cycle. -// -// errno is guaranteed to be preserved. -void MaybeSave(); - -// Causes MaybeSave to become a no-op until destroyed or reset. -class DisableSave { - public: - DisableSave(); - ~DisableSave(); - DisableSave(DisableSave const&) = delete; - DisableSave(DisableSave&&) = delete; - DisableSave& operator=(DisableSave const&) = delete; - DisableSave& operator=(DisableSave&&) = delete; - - // reset allows saves to continue, and is called implicitly by the destructor. - // It may be called multiple times safely, but is not thread-safe. - void reset(); - - private: - bool reset_ = false; -}; - -namespace internal { - -// Causes a co-operative save cycle to occur. -// -// errno is guaranteed to be preserved. -void DoCooperativeSave(); - -} // namespace internal - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_SAVE_UTIL_H_ diff --git a/test/util/save_util_linux.cc b/test/util/save_util_linux.cc deleted file mode 100644 index 57431b3ea..000000000 --- a/test/util/save_util_linux.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifdef __linux__ - -#include <errno.h> -#include <sys/syscall.h> -#include <unistd.h> - -#include "test/util/save_util.h" - -#if defined(__x86_64__) || defined(__i386__) -#define SYS_TRIGGER_SAVE SYS_create_module -#elif defined(__aarch64__) -#define SYS_TRIGGER_SAVE SYS_finit_module -#else -#error "Unknown architecture" -#endif - -namespace gvisor { -namespace testing { -namespace internal { - -void DoCooperativeSave() { - int orig_errno = errno; - // We use it to trigger saving the sentry state - // when this syscall is called. - // Notice: this needs to be a valid syscall - // that is not used in any of the syscall tests. - syscall(SYS_TRIGGER_SAVE, nullptr, 0); - errno = orig_errno; -} - -} // namespace internal -} // namespace testing -} // namespace gvisor - -#endif // __linux__ diff --git a/test/util/save_util_other.cc b/test/util/save_util_other.cc deleted file mode 100644 index 7749ded76..000000000 --- a/test/util/save_util_other.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef __linux__ - -#include "test/util/logging.h" - -namespace gvisor { -namespace testing { -namespace internal { - -void DoCooperativeSave() { - TEST_CHECK_MSG(false, "DoCooperativeSave not implemented"); -} - -} // namespace internal -} // namespace testing -} // namespace gvisor - -#endif diff --git a/test/util/signal_util.cc b/test/util/signal_util.cc deleted file mode 100644 index 5ee95ee80..000000000 --- a/test/util/signal_util.cc +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/signal_util.h" - -#include <signal.h> - -#include <ostream> - -#include "gtest/gtest.h" -#include "test/util/cleanup.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace { - -struct Range { - int start; - int end; -}; - -// Format a Range as "start-end" or "start" for single value Ranges. -static ::std::ostream& operator<<(::std::ostream& os, const Range& range) { - if (range.end > range.start) { - return os << range.start << '-' << range.end; - } - - return os << range.start; -} - -} // namespace - -// Format a sigset_t as a comma separated list of numeric ranges. -// Empty sigset: [] -// Full sigset: [1-31,34-64] -::std::ostream& operator<<(::std::ostream& os, const sigset_t& sigset) { - const char* delim = ""; - Range range = {0, 0}; - - os << '['; - - for (int sig = 1; sig <= gvisor::testing::kMaxSignal; ++sig) { - if (sigismember(&sigset, sig)) { - if (range.start) { - range.end = sig; - } else { - range.start = sig; - range.end = sig; - } - } else if (range.start) { - os << delim << range; - delim = ","; - range.start = 0; - range.end = 0; - } - } - - if (range.start) { - os << delim << range; - } - - return os << ']'; -} - -namespace gvisor { -namespace testing { - -PosixErrorOr<Cleanup> ScopedSigaction(int sig, struct sigaction const& sa) { - struct sigaction old_sa; - int rc = sigaction(sig, &sa, &old_sa); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "sigaction failed"); - } - return Cleanup([sig, old_sa] { - EXPECT_THAT(sigaction(sig, &old_sa, nullptr), SyscallSucceeds()); - }); -} - -PosixErrorOr<Cleanup> ScopedSignalMask(int how, sigset_t const& set) { - sigset_t old; - int rc = sigprocmask(how, &set, &old); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "sigprocmask failed"); - } - return Cleanup([old] { - EXPECT_THAT(sigprocmask(SIG_SETMASK, &old, nullptr), SyscallSucceeds()); - }); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/signal_util.h b/test/util/signal_util.h deleted file mode 100644 index 20eebd7e4..000000000 --- a/test/util/signal_util.h +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_SIGNAL_UTIL_H_ -#define GVISOR_TEST_UTIL_SIGNAL_UTIL_H_ - -#include <signal.h> -#include <sys/syscall.h> -#include <unistd.h> - -#include <ostream> - -#include "gmock/gmock.h" -#include "test/util/cleanup.h" -#include "test/util/posix_error.h" - -// Format a sigset_t as a comma separated list of numeric ranges. -::std::ostream& operator<<(::std::ostream& os, const sigset_t& sigset); - -namespace gvisor { -namespace testing { - -// The maximum signal number. -static constexpr int kMaxSignal = 64; - -// Wrapper for the tgkill(2) syscall, which glibc does not provide. -inline int tgkill(pid_t tgid, pid_t tid, int sig) { - return syscall(__NR_tgkill, tgid, tid, sig); -} - -// Installs the passed sigaction and returns a cleanup function to restore the -// previous handler when it goes out of scope. -PosixErrorOr<Cleanup> ScopedSigaction(int sig, struct sigaction const& sa); - -// Updates the signal mask as per sigprocmask(2) and returns a cleanup function -// to restore the previous signal mask when it goes out of scope. -PosixErrorOr<Cleanup> ScopedSignalMask(int how, sigset_t const& set); - -// ScopedSignalMask variant that creates a mask of the single signal 'sig'. -inline PosixErrorOr<Cleanup> ScopedSignalMask(int how, int sig) { - sigset_t set; - sigemptyset(&set); - sigaddset(&set, sig); - return ScopedSignalMask(how, set); -} - -// Asserts equality of two sigset_t values. -MATCHER_P(EqualsSigset, value, "equals " + ::testing::PrintToString(value)) { - for (int sig = 1; sig <= kMaxSignal; ++sig) { - if (sigismember(&arg, sig) != sigismember(&value, sig)) { - return false; - } - } - return true; -} - -#ifdef __x86_64__ -// Fault can be used to generate a synchronous SIGSEGV. -// -// This fault can be fixed up in a handler via fixup, below. -inline void Fault() { - // Zero and dereference %ax. - asm("movabs $0, %%rax\r\n" - "mov 0(%%rax), %%rax\r\n" - : - : - : "ax"); -} - -// FixupFault fixes up a fault generated by fault, above. -inline void FixupFault(ucontext_t* ctx) { - // Skip the bad instruction above. - // - // The encoding is 0x48 0xab 0x00. - ctx->uc_mcontext.gregs[REG_RIP] += 3; -} -#elif __aarch64__ -inline void Fault() { - // Zero and dereference x0. - asm("mov x0, xzr\r\n" - "str xzr, [x0]\r\n" - : - : - : "x0"); -} - -inline void FixupFault(ucontext_t* ctx) { - // Skip the bad instruction above. - ctx->uc_mcontext.pc += 4; -} -#endif - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_SIGNAL_UTIL_H_ diff --git a/test/util/temp_path.cc b/test/util/temp_path.cc deleted file mode 100644 index e1bdee7fd..000000000 --- a/test/util/temp_path.cc +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/temp_path.h" - -#include <unistd.h> - -#include <atomic> -#include <cstdlib> -#include <iostream> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -std::atomic<uint64_t> global_temp_file_number = ATOMIC_VAR_INIT(1); - -// Return a new temp filename, intended to be unique system-wide. -// -// The global file number helps maintain file naming consistency across -// different runs of a test. -// -// The timestamp is necessary because the test infrastructure invokes each -// test case in a separate process (resetting global_temp_file_number) and -// potentially in parallel, which allows for races between selecting and using a -// name. -std::string NextTempBasename() { - return absl::StrCat("gvisor_test_temp_", global_temp_file_number++, "_", - absl::ToUnixNanos(absl::Now())); -} - -void TryDeleteRecursively(std::string const& path) { - if (!path.empty()) { - int undeleted_dirs = 0; - int undeleted_files = 0; - auto status = RecursivelyDelete(path, &undeleted_dirs, &undeleted_files); - if (undeleted_dirs || undeleted_files || !status.ok()) { - std::cerr << path << ": failed to delete " << undeleted_dirs - << " directories and " << undeleted_files - << " files: " << status << std::endl; - } - } -} - -} // namespace - -constexpr mode_t TempPath::kDefaultFileMode; -constexpr mode_t TempPath::kDefaultDirMode; - -std::string NewTempAbsPathInDir(absl::string_view const dir) { - return JoinPath(dir, NextTempBasename()); -} - -std::string NewTempAbsPath() { - return NewTempAbsPathInDir(GetAbsoluteTestTmpdir()); -} - -std::string NewTempRelPath() { return NextTempBasename(); } - -std::string GetAbsoluteTestTmpdir() { - // Note that TEST_TMPDIR is guaranteed to be set. - char* env_tmpdir = getenv("TEST_TMPDIR"); - std::string tmp_dir = - env_tmpdir != nullptr ? std::string(env_tmpdir) : "/tmp"; - - return MakeAbsolute(tmp_dir, "").ValueOrDie(); -} - -PosixErrorOr<TempPath> TempPath::CreateFileWith(absl::string_view const parent, - absl::string_view const content, - mode_t const mode) { - return CreateIn(parent, [=](absl::string_view path) -> PosixError { - // CreateWithContents will call open(O_WRONLY) with the given mode. If the - // mode is not user-writable, save/restore cannot preserve the fd. Hence - // the little permission dance that's done here. - auto res = CreateWithContents(path, content, mode | 0200); - RETURN_IF_ERRNO(res); - - return Chmod(path, mode); - }); -} - -PosixErrorOr<TempPath> TempPath::CreateDirWith(absl::string_view const parent, - mode_t const mode) { - return CreateIn(parent, - [=](absl::string_view path) { return Mkdir(path, mode); }); -} - -PosixErrorOr<TempPath> TempPath::CreateSymlinkTo(absl::string_view const parent, - std::string const& dest) { - return CreateIn(parent, [=](absl::string_view path) { - int ret = symlink(dest.c_str(), std::string(path).c_str()); - if (ret != 0) { - return PosixError(errno, "symlink failed"); - } - return NoError(); - }); -} - -PosixErrorOr<TempPath> TempPath::CreateFileIn(absl::string_view const parent) { - return TempPath::CreateFileWith(parent, absl::string_view(), - kDefaultFileMode); -} - -PosixErrorOr<TempPath> TempPath::CreateDirIn(absl::string_view const parent) { - return TempPath::CreateDirWith(parent, kDefaultDirMode); -} - -PosixErrorOr<TempPath> TempPath::CreateFileMode(mode_t mode) { - return TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), absl::string_view(), - mode); -} - -PosixErrorOr<TempPath> TempPath::CreateFile() { - return TempPath::CreateFileIn(GetAbsoluteTestTmpdir()); -} - -PosixErrorOr<TempPath> TempPath::CreateDir() { - return TempPath::CreateDirIn(GetAbsoluteTestTmpdir()); -} - -TempPath::~TempPath() { TryDeleteRecursively(path_); } - -TempPath::TempPath(TempPath&& orig) { reset(orig.release()); } - -TempPath& TempPath::operator=(TempPath&& orig) { - reset(orig.release()); - return *this; -} - -std::string TempPath::reset(std::string newpath) { - std::string path = path_; - TryDeleteRecursively(path_); - path_ = std::move(newpath); - return path; -} - -std::string TempPath::release() { - std::string path = path_; - path_ = std::string(); - return path; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/temp_path.h b/test/util/temp_path.h deleted file mode 100644 index 9e5ac11f4..000000000 --- a/test/util/temp_path.h +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_TEMP_PATH_H_ -#define GVISOR_TEST_UTIL_TEMP_PATH_H_ - -#include <sys/stat.h> - -#include <string> -#include <utility> - -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -// Returns an absolute path for a file in `dir` that does not yet exist. -// Distinct calls to NewTempAbsPathInDir from the same process, even from -// multiple threads, are guaranteed to return different paths. Distinct calls to -// NewTempAbsPathInDir from different processes are not synchronized. -std::string NewTempAbsPathInDir(absl::string_view const dir); - -// Like NewTempAbsPathInDir, but the returned path is in the test's temporary -// directory, as provided by the testing framework. -std::string NewTempAbsPath(); - -// Like NewTempAbsPathInDir, but the returned path is relative (to the current -// working directory). -std::string NewTempRelPath(); - -// Returns the absolute path for the test temp dir. -std::string GetAbsoluteTestTmpdir(); - -// Represents a temporary file or directory. -class TempPath { - public: - // Default creation mode for files. - static constexpr mode_t kDefaultFileMode = 0644; - - // Default creation mode for directories. - static constexpr mode_t kDefaultDirMode = 0755; - - // Creates a temporary file in directory `parent` with mode `mode` and - // contents `content`. - static PosixErrorOr<TempPath> CreateFileWith(absl::string_view parent, - absl::string_view content, - mode_t mode); - - // Creates an empty temporary subdirectory in directory `parent` with mode - // `mode`. - static PosixErrorOr<TempPath> CreateDirWith(absl::string_view parent, - mode_t mode); - - // Creates a temporary symlink in directory `parent` to destination `dest`. - static PosixErrorOr<TempPath> CreateSymlinkTo(absl::string_view parent, - std::string const& dest); - - // Creates an empty temporary file in directory `parent` with mode - // kDefaultFileMode. - static PosixErrorOr<TempPath> CreateFileIn(absl::string_view parent); - - // Creates an empty temporary subdirectory in directory `parent` with mode - // kDefaultDirMode. - static PosixErrorOr<TempPath> CreateDirIn(absl::string_view parent); - - // Creates an empty temporary file in the test's temporary directory with mode - // `mode`. - static PosixErrorOr<TempPath> CreateFileMode(mode_t mode); - - // Creates an empty temporary file in the test's temporary directory with - // mode kDefaultFileMode. - static PosixErrorOr<TempPath> CreateFile(); - - // Creates an empty temporary subdirectory in the test's temporary directory - // with mode kDefaultDirMode. - static PosixErrorOr<TempPath> CreateDir(); - - // Constructs a TempPath that represents nothing. - TempPath() = default; - - // Constructs a TempPath that represents the given path, which will be deleted - // when the TempPath is destroyed. - explicit TempPath(std::string path) : path_(std::move(path)) {} - - // Attempts to delete the represented temporary file or directory (in the - // latter case, also attempts to delete its contents). - ~TempPath(); - - // Attempts to delete the represented temporary file or directory, then - // transfers ownership of the path represented by orig to this TempPath. - TempPath(TempPath&& orig); - TempPath& operator=(TempPath&& orig); - - // Changes the path this TempPath represents. If the TempPath already - // represented a path, deletes and returns that path. Otherwise returns the - // empty string. - std::string reset(std::string newpath); - std::string reset() { return reset(""); } - - // Forgets and returns the path this TempPath represents. The path is not - // deleted. - std::string release(); - - // Returns the path this TempPath represents. - std::string path() const { return path_; } - - private: - template <typename F> - static PosixErrorOr<TempPath> CreateIn(absl::string_view const parent, - F const& f) { - std::string path = NewTempAbsPathInDir(parent); - RETURN_IF_ERRNO(f(path)); - return TempPath(std::move(path)); - } - - std::string path_; -}; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_TEMP_PATH_H_ diff --git a/test/util/temp_umask.h b/test/util/temp_umask.h deleted file mode 100644 index e7de84a54..000000000 --- a/test/util/temp_umask.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_TEMP_UMASK_H_ -#define GVISOR_TEST_UTIL_TEMP_UMASK_H_ - -#include <sys/stat.h> -#include <sys/types.h> - -namespace gvisor { -namespace testing { - -class TempUmask { - public: - // Sets the process umask to `mask`. - explicit TempUmask(mode_t mask) : old_mask_(umask(mask)) {} - - // Sets the process umask to its previous value. - ~TempUmask() { umask(old_mask_); } - - private: - mode_t old_mask_; -}; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_TEMP_UMASK_H_ diff --git a/test/util/test_main.cc b/test/util/test_main.cc deleted file mode 100644 index 1f389e58f..000000000 --- a/test/util/test_main.cc +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/test_util.h" - -int main(int argc, char** argv) { - gvisor::testing::TestInit(&argc, &argv); - return gvisor::testing::RunAllTests(); -} diff --git a/test/util/test_util.cc b/test/util/test_util.cc deleted file mode 100644 index d0c1d6426..000000000 --- a/test/util/test_util.cc +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/test_util.h" - -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <sys/utsname.h> -#include <unistd.h> - -#include <ctime> -#include <iostream> -#include <vector> - -#include "absl/base/attributes.h" -#include "absl/flags/flag.h" // IWYU pragma: keep -#include "absl/flags/parse.h" // IWYU pragma: keep -#include "absl/strings/numbers.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_split.h" -#include "absl/time/time.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -constexpr char kGvisorNetwork[] = "GVISOR_NETWORK"; -constexpr char kGvisorVfs[] = "GVISOR_VFS"; -constexpr char kFuseEnabled[] = "FUSE_ENABLED"; - -bool IsRunningOnGvisor() { return GvisorPlatform() != Platform::kNative; } - -const std::string GvisorPlatform() { - // Set by runner.go. - const char* env = getenv(kTestOnGvisor); - if (!env) { - return Platform::kNative; - } - return std::string(env); -} - -bool IsRunningWithHostinet() { - const char* env = getenv(kGvisorNetwork); - return env && strcmp(env, "host") == 0; -} - -bool IsRunningWithVFS1() { - const char* env = getenv(kGvisorVfs); - if (env == nullptr) { - // If not set, it's running on Linux. - return false; - } - return strcmp(env, "VFS1") == 0; -} - -bool IsFUSEEnabled() { - const char* env = getenv(kFuseEnabled); - return env && strcmp(env, "TRUE") == 0; -} - -// Inline cpuid instruction. Preserve %ebx/%rbx register. In PIC compilations -// %ebx contains the address of the global offset table. %rbx is occasionally -// used to address stack variables in presence of dynamic allocas. -#if defined(__x86_64__) -#define GETCPUID(a, b, c, d, a_inp, c_inp) \ - asm("mov %%rbx, %%rdi\n" \ - "cpuid\n" \ - "xchg %%rdi, %%rbx\n" \ - : "=a"(a), "=D"(b), "=c"(c), "=d"(d) \ - : "a"(a_inp), "2"(c_inp)) - -CPUVendor GetCPUVendor() { - uint32_t eax, ebx, ecx, edx; - std::string vendor_str; - // Get vendor string (issue CPUID with eax = 0) - GETCPUID(eax, ebx, ecx, edx, 0, 0); - vendor_str.append(reinterpret_cast<char*>(&ebx), 4); - vendor_str.append(reinterpret_cast<char*>(&edx), 4); - vendor_str.append(reinterpret_cast<char*>(&ecx), 4); - if (vendor_str == "GenuineIntel") { - return CPUVendor::kIntel; - } else if (vendor_str == "AuthenticAMD") { - return CPUVendor::kAMD; - } - return CPUVendor::kUnknownVendor; -} -#endif // defined(__x86_64__) - -bool operator==(const KernelVersion& first, const KernelVersion& second) { - return first.major == second.major && first.minor == second.minor && - first.micro == second.micro; -} - -PosixErrorOr<KernelVersion> ParseKernelVersion(absl::string_view vers_str) { - KernelVersion version = {}; - std::vector<std::string> values = - absl::StrSplit(vers_str, absl::ByAnyChar(".-")); - if (values.size() == 2) { - ASSIGN_OR_RETURN_ERRNO(version.major, Atoi<int>(values[0])); - ASSIGN_OR_RETURN_ERRNO(version.minor, Atoi<int>(values[1])); - return version; - } else if (values.size() >= 3) { - ASSIGN_OR_RETURN_ERRNO(version.major, Atoi<int>(values[0])); - ASSIGN_OR_RETURN_ERRNO(version.minor, Atoi<int>(values[1])); - ASSIGN_OR_RETURN_ERRNO(version.micro, Atoi<int>(values[2])); - return version; - } - return PosixError(EINVAL, absl::StrCat("Unknown kernel release: ", vers_str)); -} - -PosixErrorOr<KernelVersion> GetKernelVersion() { - utsname buf; - RETURN_ERROR_IF_SYSCALL_FAIL(uname(&buf)); - return ParseKernelVersion(buf.release); -} - -std::string CPUSetToString(const cpu_set_t& set, size_t cpus) { - std::string str = "cpuset["; - for (unsigned int n = 0; n < cpus; n++) { - if (CPU_ISSET(n, &set)) { - if (n != 0) { - absl::StrAppend(&str, " "); - } - absl::StrAppend(&str, n); - } - } - absl::StrAppend(&str, "]"); - return str; -} - -// An overloaded operator<< makes it easy to dump the value of an OpenFd. -std::ostream& operator<<(std::ostream& out, OpenFd const& ofd) { - out << ofd.fd << " -> " << ofd.link; - return out; -} - -// An overloaded operator<< makes it easy to dump a vector of OpenFDs. -std::ostream& operator<<(std::ostream& out, std::vector<OpenFd> const& v) { - for (const auto& ofd : v) { - out << ofd << std::endl; - } - return out; -} - -PosixErrorOr<std::vector<OpenFd>> GetOpenFDs() { - // Get the results from /proc/self/fd. - ASSIGN_OR_RETURN_ERRNO(auto dir_list, - ListDir("/proc/self/fd", /*skipdots=*/true)); - - std::vector<OpenFd> ret_fds; - for (const auto& str_fd : dir_list) { - OpenFd open_fd = {}; - ASSIGN_OR_RETURN_ERRNO(open_fd.fd, Atoi<int>(str_fd)); - std::string path = absl::StrCat("/proc/self/fd/", open_fd.fd); - - // Resolve the link. - char buf[PATH_MAX] = {}; - int ret = readlink(path.c_str(), buf, sizeof(buf)); - if (ret < 0) { - if (errno == ENOENT) { - // The FD may have been closed, let's be resilient. - continue; - } - - return PosixError( - errno, absl::StrCat("readlink of ", path, " returned errno ", errno)); - } - open_fd.link = std::string(buf, ret); - ret_fds.emplace_back(std::move(open_fd)); - } - return ret_fds; -} - -PosixErrorOr<uint64_t> Links(const std::string& path) { - struct stat st; - if (stat(path.c_str(), &st)) { - return PosixError(errno, absl::StrCat("Failed to stat ", path)); - } - return static_cast<uint64_t>(st.st_nlink); -} - -void RandomizeBuffer(void* buffer, size_t len) { - struct timespec ts = {}; - clock_gettime(CLOCK_MONOTONIC, &ts); - uint32_t seed = static_cast<uint32_t>(ts.tv_nsec); - char* const buf = static_cast<char*>(buffer); - for (size_t i = 0; i < len; i++) { - buf[i] = rand_r(&seed) % 255; - } -} - -std::vector<std::vector<struct iovec>> GenerateIovecs(uint64_t total_size, - void* buf, - size_t buflen) { - std::vector<std::vector<struct iovec>> result; - for (uint64_t offset = 0; offset < total_size;) { - auto& iovec_array = *result.emplace(result.end()); - - for (; offset < total_size && iovec_array.size() < IOV_MAX; - offset += buflen) { - struct iovec iov = {}; - iov.iov_base = buf; - iov.iov_len = std::min<uint64_t>(total_size - offset, buflen); - iovec_array.push_back(iov); - } - } - - return result; -} - -uint64_t Megabytes(uint64_t n) { - // Overflow check, upper 20 bits in n shouldn't be set. - TEST_CHECK(!(0xfffff00000000000 & n)); - return n << 20; -} - -bool Equivalent(uint64_t current, uint64_t target, double tolerance) { - auto abs_diff = target > current ? target - current : current - target; - return abs_diff <= static_cast<uint64_t>(tolerance * target); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/test_util.h b/test/util/test_util.h deleted file mode 100644 index bcbb388ed..000000000 --- a/test/util/test_util.h +++ /dev/null @@ -1,816 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Utilities for syscall testing. -// -// Initialization -// ============== -// -// Prior to calling RUN_ALL_TESTS, all tests must use TestInit(&argc, &argv). -// See the TestInit function for exact side-effects and semantics. -// -// Configuration -// ============= -// -// IsRunningOnGvisor returns true if the test is known to be running on gVisor. -// GvisorPlatform can be used to get more detail: -// -// if (GvisorPlatform() == Platform::kPtrace) { -// ... -// } -// -// SetupGvisorDeathTest ensures that signal handling does not interfere with -/// tests that rely on fatal signals. -// -// Matchers -// ======== -// -// ElementOf(xs) matches if the matched value is equal to an element of the -// container xs. Example: -// -// // PASS -// EXPECT_THAT(1, ElementOf({0, 1, 2})); -// -// // FAIL -// // Value of: 3 -// // Expected: one of {0, 1, 2} -// // Actual: 3 -// EXPECT_THAT(3, ElementOf({0, 1, 2})); -// -// SyscallSucceeds() matches if the syscall is successful. A successful syscall -// is defined by either a return value not equal to -1, or a return value of -1 -// with an errno of 0 (which is a possible successful return for e.g. -// PTRACE_PEEK). Example: -// -// // PASS -// EXPECT_THAT(open("/dev/null", O_RDONLY), SyscallSucceeds()); -// -// // FAIL -// // Value of: open("/", O_RDWR) -// // Expected: not -1 (success) -// // Actual: -1 (of type int), with errno 21 (Is a directory) -// EXPECT_THAT(open("/", O_RDWR), SyscallSucceeds()); -// -// SyscallSucceedsWithValue(m) matches if the syscall is successful, and the -// value also matches m. Example: -// -// // PASS -// EXPECT_THAT(read(4, buf, 8192), SyscallSucceedsWithValue(8192)); -// -// // FAIL -// // Value of: read(-1, buf, 8192) -// // Expected: is equal to 8192 -// // Actual: -1 (of type long), with errno 9 (Bad file number) -// EXPECT_THAT(read(-1, buf, 8192), SyscallSucceedsWithValue(8192)); -// -// // FAIL -// // Value of: read(4, buf, 1) -// // Expected: is > 4096 -// // Actual: 1 (of type long) -// EXPECT_THAT(read(4, buf, 1), SyscallSucceedsWithValue(Gt(4096))); -// -// SyscallFails() matches if the syscall is unsuccessful. An unsuccessful -// syscall is defined by a return value of -1 with a non-zero errno. Example: -// -// // PASS -// EXPECT_THAT(open("/", O_RDWR), SyscallFails()); -// -// // FAIL -// // Value of: open("/dev/null", O_RDONLY) -// // Expected: -1 (failure) -// // Actual: 0 (of type int) -// EXPECT_THAT(open("/dev/null", O_RDONLY), SyscallFails()); -// -// SyscallFailsWithErrno(m) matches if the syscall is unsuccessful, and errno -// matches m. Example: -// -// // PASS -// EXPECT_THAT(open("/", O_RDWR), SyscallFailsWithErrno(EISDIR)); -// -// // PASS -// EXPECT_THAT(open("/etc/passwd", O_RDWR | O_DIRECTORY), -// SyscallFailsWithErrno(AnyOf(EACCES, ENOTDIR))); -// -// // FAIL -// // Value of: open("/dev/null", O_RDONLY) -// // Expected: -1 (failure) with errno 21 (Is a directory) -// // Actual: 0 (of type int) -// EXPECT_THAT(open("/dev/null", O_RDONLY), SyscallFailsWithErrno(EISDIR)); -// -// // FAIL -// // Value of: open("/", O_RDWR) -// // Expected: -1 (failure) with errno 22 (Invalid argument) -// // Actual: -1 (of type int), failure, but with errno 21 (Is a directory) -// EXPECT_THAT(open("/", O_RDWR), SyscallFailsWithErrno(EINVAL)); -// -// Because the syscall matchers encode save/restore functionality, their meaning -// should not be inverted via Not. That is, AnyOf(SyscallSucceedsWithValue(1), -// SyscallSucceedsWithValue(2)) is permitted, but not -// Not(SyscallFailsWithErrno(EPERM)). -// -// Syscalls -// ======== -// -// RetryEINTR wraps a function that returns -1 and sets errno on failure -// to be automatically retried when EINTR occurs. Example: -// -// auto rv = RetryEINTR(waitpid)(pid, &status, 0); -// -// ReadFd/WriteFd/PreadFd/PwriteFd are interface-compatible wrappers around the -// read/write/pread/pwrite syscalls to handle both EINTR and partial -// reads/writes. Example: -// -// EXPECT_THAT(ReadFd(fd, &buf, size), SyscallSucceedsWithValue(size)); -// -// General Utilities -// ================= -// -// ApplyVec(f, xs) returns a vector containing the result of applying function -// `f` to each value in `xs`. -// -// AllBitwiseCombinations takes a variadic number of ranges containing integers -// and returns a vector containing every integer that can be formed by ORing -// together exactly one integer from each list. List<T> is an alias for -// std::initializer_list<T> that makes AllBitwiseCombinations more ergonomic to -// use with list literals (initializer lists do not otherwise participate in -// template argument deduction). Example: -// -// EXPECT_THAT( -// AllBitwiseCombinations<int>( -// List<int>{SOCK_DGRAM, SOCK_STREAM}, -// List<int>{0, SOCK_NONBLOCK}), -// Contains({SOCK_DGRAM, SOCK_STREAM, SOCK_DGRAM | SOCK_NONBLOCK, -// SOCK_STREAM | SOCK_NONBLOCK})); -// -// VecCat takes a variadic number of containers and returns a vector containing -// the concatenated contents. -// -// VecAppend takes an initial container and a variadic number of containers and -// appends each to the initial container. -// -// RandomizeBuffer will use MTRandom to fill the given buffer with random bytes. -// -// GenerateIovecs will return the smallest number of iovec arrays for writing a -// given total number of bytes to a file, each iovec array size up to IOV_MAX, -// each iovec in each array pointing to the same buffer. - -#ifndef GVISOR_TEST_UTIL_TEST_UTIL_H_ -#define GVISOR_TEST_UTIL_TEST_UTIL_H_ - -#include <stddef.h> -#include <stdlib.h> -#include <sys/uio.h> -#include <time.h> -#include <unistd.h> - -#include <algorithm> -#include <cerrno> -#include <initializer_list> -#include <iterator> -#include <string> -#include <thread> // NOLINT: using std::thread::hardware_concurrency(). -#include <utility> -#include <vector> - -#include "gmock/gmock.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_format.h" -#include "absl/strings/string_view.h" -#include "absl/time/time.h" -#include "test/util/fs_util.h" -#include "test/util/logging.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" - -namespace gvisor { -namespace testing { - -constexpr char kTestOnGvisor[] = "TEST_ON_GVISOR"; - -// TestInit must be called prior to RUN_ALL_TESTS. -// -// This parses all arguments and adjusts argc and argv appropriately. -// -// TestInit may create background threads. -void TestInit(int* argc, char*** argv); - -// SKIP_IF may be used to skip a test case. -// -// These cases are still emitted, but a SKIPPED line will appear. -#define SKIP_IF(expr) \ - do { \ - if (expr) GTEST_SKIP() << #expr; \ - } while (0) - -// Platform contains platform names. -namespace Platform { -constexpr char kNative[] = "native"; -constexpr char kPtrace[] = "ptrace"; -constexpr char kKVM[] = "kvm"; -constexpr char kFuchsia[] = "fuchsia"; -} // namespace Platform - -bool IsRunningOnGvisor(); -const std::string GvisorPlatform(); -bool IsRunningWithHostinet(); -// TODO(gvisor.dev/issue/1624): Delete once VFS1 is gone. -bool IsRunningWithVFS1(); -bool IsFUSEEnabled(); - -#ifdef __linux__ -void SetupGvisorDeathTest(); -#endif - -struct KernelVersion { - int major; - int minor; - int micro; -}; - -bool operator==(const KernelVersion& first, const KernelVersion& second); - -PosixErrorOr<KernelVersion> ParseKernelVersion(absl::string_view vers_string); -PosixErrorOr<KernelVersion> GetKernelVersion(); - -static const size_t kPageSize = sysconf(_SC_PAGESIZE); - -enum class CPUVendor { kIntel, kAMD, kUnknownVendor }; - -CPUVendor GetCPUVendor(); - -inline int NumCPUs() { return std::thread::hardware_concurrency(); } - -// Converts cpu_set_t to a std::string for easy examination. -std::string CPUSetToString(const cpu_set_t& set, size_t cpus = CPU_SETSIZE); - -struct OpenFd { - // fd is the open file descriptor number. - int fd = -1; - - // link is the resolution of the symbolic link. - std::string link; -}; - -// Make it easier to log OpenFds to error streams. -std::ostream& operator<<(std::ostream& out, std::vector<OpenFd> const& v); -std::ostream& operator<<(std::ostream& out, OpenFd const& ofd); - -// Gets a detailed list of open fds for this process. -PosixErrorOr<std::vector<OpenFd>> GetOpenFDs(); - -// Returns the number of hard links to a path. -PosixErrorOr<uint64_t> Links(const std::string& path); - -inline uint64_t ns_elapsed(const struct timespec& begin, - const struct timespec& end) { - return (end.tv_sec - begin.tv_sec) * 1000000000 + - (end.tv_nsec - begin.tv_nsec); -} - -inline uint64_t ms_elapsed(const struct timespec& begin, - const struct timespec& end) { - return ns_elapsed(begin, end) / 1000000; -} - -namespace internal { - -template <typename Container> -class ElementOfMatcher { - public: - explicit ElementOfMatcher(Container container) - : container_(::std::move(container)) {} - - template <typename T> - bool MatchAndExplain(T const& rv, - ::testing::MatchResultListener* const listener) const { - using std::count; - return count(container_.begin(), container_.end(), rv) != 0; - } - - void DescribeTo(::std::ostream* const os) const { - *os << "one of {"; - char const* sep = ""; - for (auto const& elem : container_) { - *os << sep << elem; - sep = ", "; - } - *os << "}"; - } - - void DescribeNegationTo(::std::ostream* const os) const { - *os << "none of {"; - char const* sep = ""; - for (auto const& elem : container_) { - *os << sep << elem; - sep = ", "; - } - *os << "}"; - } - - private: - Container const container_; -}; - -template <typename E> -class SyscallSuccessMatcher { - public: - explicit SyscallSuccessMatcher(E expected) - : expected_(::std::move(expected)) {} - - template <typename T> - operator ::testing::Matcher<T>() const { - // E is one of three things: - // - T, or a type losslessly and implicitly convertible to T. - // - A monomorphic Matcher<T>. - // - A polymorphic matcher. - // SafeMatcherCast handles any of the above correctly. - // - // Similarly, gMock will invoke this conversion operator to obtain a - // monomorphic matcher (this is how polymorphic matchers are implemented). - return ::testing::MakeMatcher( - new Impl<T>(::testing::SafeMatcherCast<T>(expected_))); - } - - private: - template <typename T> - class Impl : public ::testing::MatcherInterface<T> { - public: - explicit Impl(::testing::Matcher<T> matcher) - : matcher_(::std::move(matcher)) {} - - bool MatchAndExplain( - T const& rv, - ::testing::MatchResultListener* const listener) const override { - if (rv == static_cast<decltype(rv)>(-1) && errno != 0) { - *listener << "with errno " << PosixError(errno); - return false; - } - bool match = matcher_.MatchAndExplain(rv, listener); - if (match) { - MaybeSave(); - } - return match; - } - - void DescribeTo(::std::ostream* const os) const override { - matcher_.DescribeTo(os); - } - - void DescribeNegationTo(::std::ostream* const os) const override { - matcher_.DescribeNegationTo(os); - } - - private: - ::testing::Matcher<T> matcher_; - }; - - private: - E expected_; -}; - -// A polymorphic matcher equivalent to ::testing::internal::AnyMatcher, except -// not in namespace ::testing::internal, and describing SyscallSucceeds()'s -// match constraints (which are enforced by SyscallSuccessMatcher::Impl). -class AnySuccessValueMatcher { - public: - template <typename T> - operator ::testing::Matcher<T>() const { - return ::testing::MakeMatcher(new Impl<T>()); - } - - private: - template <typename T> - class Impl : public ::testing::MatcherInterface<T> { - public: - bool MatchAndExplain( - T const& rv, - ::testing::MatchResultListener* const listener) const override { - return true; - } - - void DescribeTo(::std::ostream* const os) const override { - *os << "not -1 (success)"; - } - - void DescribeNegationTo(::std::ostream* const os) const override { - *os << "-1 (failure)"; - } - }; -}; - -class SyscallFailureMatcher { - public: - explicit SyscallFailureMatcher(::testing::Matcher<int> errno_matcher) - : errno_matcher_(std::move(errno_matcher)) {} - - template <typename T> - bool MatchAndExplain(T const& rv, - ::testing::MatchResultListener* const listener) const { - if (rv != static_cast<decltype(rv)>(-1)) { - return false; - } - int actual_errno = errno; - *listener << "with errno " << PosixError(actual_errno); - bool match = errno_matcher_.MatchAndExplain(actual_errno, listener); - if (match) { - MaybeSave(); - } - return match; - } - - void DescribeTo(::std::ostream* const os) const { - *os << "-1 (failure), with errno "; - errno_matcher_.DescribeTo(os); - } - - void DescribeNegationTo(::std::ostream* const os) const { - *os << "not -1 (success), with errno "; - errno_matcher_.DescribeNegationTo(os); - } - - private: - ::testing::Matcher<int> errno_matcher_; -}; - -class SpecificErrnoMatcher : public ::testing::MatcherInterface<int> { - public: - explicit SpecificErrnoMatcher(int const expected) : expected_(expected) {} - - bool MatchAndExplain( - int const actual_errno, - ::testing::MatchResultListener* const listener) const override { - return actual_errno == expected_; - } - - void DescribeTo(::std::ostream* const os) const override { - *os << PosixError(expected_); - } - - void DescribeNegationTo(::std::ostream* const os) const override { - *os << "not " << PosixError(expected_); - } - - private: - int const expected_; -}; - -inline ::testing::Matcher<int> SpecificErrno(int const expected) { - return ::testing::MakeMatcher(new SpecificErrnoMatcher(expected)); -} - -} // namespace internal - -template <typename Container> -inline ::testing::PolymorphicMatcher<internal::ElementOfMatcher<Container>> -ElementOf(Container container) { - return ::testing::MakePolymorphicMatcher( - internal::ElementOfMatcher<Container>(::std::move(container))); -} - -template <typename T> -inline ::testing::PolymorphicMatcher< - internal::ElementOfMatcher<::std::vector<T>>> -ElementOf(::std::initializer_list<T> elems) { - return ::testing::MakePolymorphicMatcher( - internal::ElementOfMatcher<::std::vector<T>>(::std::vector<T>(elems))); -} - -template <typename E> -inline internal::SyscallSuccessMatcher<E> SyscallSucceedsWithValue(E expected) { - return internal::SyscallSuccessMatcher<E>(::std::move(expected)); -} - -inline internal::SyscallSuccessMatcher<internal::AnySuccessValueMatcher> -SyscallSucceeds() { - return SyscallSucceedsWithValue( - ::gvisor::testing::internal::AnySuccessValueMatcher()); -} - -inline ::testing::PolymorphicMatcher<internal::SyscallFailureMatcher> -SyscallFailsWithErrno(::testing::Matcher<int> expected) { - return ::testing::MakePolymorphicMatcher( - internal::SyscallFailureMatcher(::std::move(expected))); -} - -// Overload taking an int so that SyscallFailsWithErrno(<specific errno>) uses -// internal::SpecificErrno (which stringifies the errno) rather than -// ::testing::Eq (which doesn't). -inline ::testing::PolymorphicMatcher<internal::SyscallFailureMatcher> -SyscallFailsWithErrno(int const expected) { - return SyscallFailsWithErrno(internal::SpecificErrno(expected)); -} - -inline ::testing::PolymorphicMatcher<internal::SyscallFailureMatcher> -SyscallFails() { - return SyscallFailsWithErrno(::testing::Gt(0)); -} - -// As of GCC 7.2, -Wall => -Wc++17-compat => -Wnoexcept-type generates an -// irrelevant, non-actionable warning about ABI compatibility when -// RetryEINTRImpl is constructed with a noexcept function, such as glibc's -// syscall(). See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80985. -#if defined(__GNUC__) && !defined(__clang__) && \ - (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2)) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnoexcept-type" -#endif - -namespace internal { - -template <typename F> -struct RetryEINTRImpl { - F const f; - - explicit constexpr RetryEINTRImpl(F f) : f(std::move(f)) {} - - template <typename... Args> - auto operator()(Args&&... args) const - -> decltype(f(std::forward<Args>(args)...)) { - while (true) { - errno = 0; - auto const ret = f(std::forward<Args>(args)...); - if (ret != -1 || errno != EINTR) { - return ret; - } - } - } -}; - -} // namespace internal - -template <typename F> -constexpr internal::RetryEINTRImpl<F> RetryEINTR(F&& f) { - return internal::RetryEINTRImpl<F>(std::forward<F>(f)); -} - -#if defined(__GNUC__) && !defined(__clang__) && \ - (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2)) -#pragma GCC diagnostic pop -#endif - -namespace internal { - -template <typename F> -ssize_t ApplyFileIoSyscall(F const& f, size_t const count) { - size_t completed = 0; - // `do ... while` because some callers actually want to make a syscall with a - // count of 0. - do { - auto const cur = RetryEINTR(f)(completed); - if (cur < 0) { - return cur; - } else if (cur == 0) { - break; - } - completed += cur; - } while (completed < count); - return completed; -} - -} // namespace internal - -inline PosixErrorOr<std::string> ReadAllFd(int fd) { - std::string all; - all.reserve(128 * 1024); // arbitrary. - - std::vector<char> buffer(16 * 1024); - for (;;) { - auto const bytes = RetryEINTR(read)(fd, buffer.data(), buffer.size()); - if (bytes < 0) { - return PosixError(errno, "file read"); - } - if (bytes == 0) { - return std::move(all); - } - if (bytes > 0) { - all.append(buffer.data(), bytes); - } - } -} - -inline ssize_t ReadFd(int fd, void* buf, size_t count) { - return internal::ApplyFileIoSyscall( - [&](size_t completed) { - return read(fd, static_cast<char*>(buf) + completed, count - completed); - }, - count); -} - -inline ssize_t WriteFd(int fd, void const* buf, size_t count) { - return internal::ApplyFileIoSyscall( - [&](size_t completed) { - return write(fd, static_cast<char const*>(buf) + completed, - count - completed); - }, - count); -} - -inline ssize_t PreadFd(int fd, void* buf, size_t count, off_t offset) { - return internal::ApplyFileIoSyscall( - [&](size_t completed) { - return pread(fd, static_cast<char*>(buf) + completed, count - completed, - offset + completed); - }, - count); -} - -inline ssize_t PwriteFd(int fd, void const* buf, size_t count, off_t offset) { - return internal::ApplyFileIoSyscall( - [&](size_t completed) { - return pwrite(fd, static_cast<char const*>(buf) + completed, - count - completed, offset + completed); - }, - count); -} - -template <typename T> -using List = std::initializer_list<T>; - -namespace internal { - -template <typename T> -void AppendAllBitwiseCombinations(std::vector<T>* combinations, T current) { - combinations->push_back(current); -} - -template <typename T, typename Arg, typename... Args> -void AppendAllBitwiseCombinations(std::vector<T>* combinations, T current, - Arg&& next, Args&&... rest) { - for (auto const option : next) { - AppendAllBitwiseCombinations(combinations, current | option, rest...); - } -} - -inline size_t CombinedSize(size_t accum) { return accum; } - -template <typename T, typename... Args> -size_t CombinedSize(size_t accum, T const& x, Args&&... xs) { - return CombinedSize(accum + x.size(), std::forward<Args>(xs)...); -} - -// Base case: no more containers, so do nothing. -template <typename T> -void DoMoveExtendContainer(T* c) {} - -// Append each container next to c. -template <typename T, typename U, typename... Args> -void DoMoveExtendContainer(T* c, U&& next, Args&&... rest) { - std::move(std::begin(next), std::end(next), std::back_inserter(*c)); - DoMoveExtendContainer(c, std::forward<Args>(rest)...); -} - -} // namespace internal - -template <typename T = int> -std::vector<T> AllBitwiseCombinations() { - return std::vector<T>(); -} - -template <typename T = int, typename... Args> -std::vector<T> AllBitwiseCombinations(Args&&... args) { - std::vector<T> combinations; - internal::AppendAllBitwiseCombinations(&combinations, 0, args...); - return combinations; -} - -template <typename T, typename U, typename F> -std::vector<T> ApplyVec(F const& f, std::vector<U> const& us) { - std::vector<T> vec; - vec.reserve(us.size()); - for (auto const& u : us) { - vec.push_back(f(u)); - } - return vec; -} - -template <typename T, typename U> -std::vector<T> ApplyVecToVec(std::vector<std::function<T(U)>> const& fs, - std::vector<U> const& us) { - std::vector<T> vec; - vec.reserve(us.size() * fs.size()); - for (auto const& f : fs) { - for (auto const& u : us) { - vec.push_back(f(u)); - } - } - return vec; -} - -// Moves all elements from the containers `args` to the end of `c`. -template <typename T, typename... Args> -void VecAppend(T* c, Args&&... args) { - c->reserve(internal::CombinedSize(c->size(), args...)); - internal::DoMoveExtendContainer(c, std::forward<Args>(args)...); -} - -// Returns a vector containing the concatenated contents of the containers -// `args`. -template <typename T, typename... Args> -std::vector<T> VecCat(Args&&... args) { - std::vector<T> combined; - VecAppend(&combined, std::forward<Args>(args)...); - return combined; -} - -#define RETURN_ERROR_IF_SYSCALL_FAIL(syscall) \ - do { \ - if ((syscall) < 0 && errno != 0) { \ - return PosixError(errno, #syscall); \ - } \ - } while (false) - -// Fill the given buffer with random bytes. -void RandomizeBuffer(void* buffer, size_t len); - -template <typename T> -inline PosixErrorOr<T> Atoi(absl::string_view str) { - T ret; - if (!absl::SimpleAtoi<T>(str, &ret)) { - return PosixError(EINVAL, "String not a number."); - } - return ret; -} - -inline PosixErrorOr<uint64_t> AtoiBase(absl::string_view str, int base) { - if (base > 255 || base < 2) { - return PosixError(EINVAL, "Invalid Base"); - } - - uint64_t ret = 0; - if (!absl::numbers_internal::safe_strtou64_base(str, &ret, base)) { - return PosixError(EINVAL, "String not a number."); - } - - return ret; -} - -inline PosixErrorOr<double> Atod(absl::string_view str) { - double ret; - if (!absl::SimpleAtod(str, &ret)) { - return PosixError(EINVAL, "String not a double type."); - } - return ret; -} - -inline PosixErrorOr<float> Atof(absl::string_view str) { - float ret; - if (!absl::SimpleAtof(str, &ret)) { - return PosixError(EINVAL, "String not a float type."); - } - return ret; -} - -// Return the smallest number of iovec arrays that can be used to write -// "total_bytes" number of bytes, each iovec writing one "buf". -std::vector<std::vector<struct iovec>> GenerateIovecs(uint64_t total_size, - void* buf, size_t buflen); - -// Returns bytes in 'n' megabytes. Used for readability. -uint64_t Megabytes(uint64_t n); - -// Predicate for checking that a value is within some tolerance of another -// value. Returns true iff current is in the range [target * (1 - tolerance), -// target * (1 + tolerance)]. -bool Equivalent(uint64_t current, uint64_t target, double tolerance); - -// Matcher wrapping the Equivalent predicate. -MATCHER_P2(EquivalentWithin, target, tolerance, - std::string(negation ? "Isn't" : "Is") + - ::absl::StrFormat(" within %.2f%% of the target of %zd bytes", - tolerance * 100, target)) { - if (target == 0) { - *result_listener << ::absl::StreamFormat("difference of infinity%%"); - } else { - int64_t delta = static_cast<int64_t>(arg) - static_cast<int64_t>(target); - double delta_percent = - static_cast<double>(delta) / static_cast<double>(target) * 100; - *result_listener << ::absl::StreamFormat("difference of %.2f%%", - delta_percent); - } - return Equivalent(arg, target, tolerance); -} - -// Returns the absolute path to the a data dependency. 'path' is the runfile -// location relative to workspace root. -#ifdef __linux__ -std::string RunfilePath(std::string path); -#endif - -void TestInit(int* argc, char*** argv); -int RunAllTests(void); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_TEST_UTIL_H_ diff --git a/test/util/test_util_impl.cc b/test/util/test_util_impl.cc deleted file mode 100644 index 7e1ad9e66..000000000 --- a/test/util/test_util_impl.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <signal.h> - -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "absl/flags/parse.h" -#include "benchmark/benchmark.h" -#include "test/util/logging.h" - -extern bool FLAGS_benchmark_list_tests; -extern std::string FLAGS_benchmark_filter; - -namespace gvisor { -namespace testing { - -void SetupGvisorDeathTest() {} - -void TestInit(int* argc, char*** argv) { - ::testing::InitGoogleTest(argc, *argv); - benchmark::Initialize(argc, *argv); - ::absl::ParseCommandLine(*argc, *argv); - - // Always mask SIGPIPE as it's common and tests aren't expected to handle it. - struct sigaction sa = {}; - sa.sa_handler = SIG_IGN; - TEST_CHECK(sigaction(SIGPIPE, &sa, nullptr) == 0); -} - -int RunAllTests() { - if (FLAGS_benchmark_list_tests || FLAGS_benchmark_filter != ".") { - benchmark::RunSpecifiedBenchmarks(); - return 0; - } else { - return RUN_ALL_TESTS(); - } -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/test_util_runfiles.cc b/test/util/test_util_runfiles.cc deleted file mode 100644 index 7210094eb..000000000 --- a/test/util/test_util_runfiles.cc +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <iostream> -#include <string> - -#include "test/util/fs_util.h" -#include "test/util/test_util.h" -#include "tools/cpp/runfiles/runfiles.h" - -namespace gvisor { -namespace testing { - -std::string RunfilePath(std::string path) { - static const bazel::tools::cpp::runfiles::Runfiles* const runfiles = [] { - std::string error; - auto* runfiles = - bazel::tools::cpp::runfiles::Runfiles::CreateForTest(&error); - if (runfiles == nullptr) { - std::cerr << "Unable to find runfiles: " << error << std::endl; - } - return runfiles; - }(); - - if (!runfiles) { - // Can't find runfiles? This probably won't work, but __main__/path is our - // best guess. - return JoinPath("__main__", path); - } - - return runfiles->Rlocation(JoinPath("__main__", path)); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/test_util_test.cc b/test/util/test_util_test.cc deleted file mode 100644 index f42100374..000000000 --- a/test/util/test_util_test.cc +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/test_util.h" - -#include <errno.h> - -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -using ::testing::AnyOf; -using ::testing::Gt; -using ::testing::IsEmpty; -using ::testing::Lt; -using ::testing::Not; -using ::testing::TypedEq; -using ::testing::UnorderedElementsAre; -using ::testing::UnorderedElementsAreArray; - -namespace gvisor { -namespace testing { - -namespace { - -TEST(KernelVersionParsing, ValidateParsing) { - KernelVersion v = ASSERT_NO_ERRNO_AND_VALUE( - ParseKernelVersion("4.18.10-1foo2-amd64 baz blah")); - ASSERT_TRUE(v == KernelVersion({4, 18, 10})); - - v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.18.10-1foo2-amd64")); - ASSERT_TRUE(v == KernelVersion({4, 18, 10})); - - v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.18.10-14-amd64")); - ASSERT_TRUE(v == KernelVersion({4, 18, 10})); - - v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.18.10-amd64")); - ASSERT_TRUE(v == KernelVersion({4, 18, 10})); - - v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.18.10")); - ASSERT_TRUE(v == KernelVersion({4, 18, 10})); - - v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.0.10")); - ASSERT_TRUE(v == KernelVersion({4, 0, 10})); - - v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.0")); - ASSERT_TRUE(v == KernelVersion({4, 0, 0})); - - ASSERT_THAT(ParseKernelVersion("4.a"), PosixErrorIs(EINVAL, ::testing::_)); - ASSERT_THAT(ParseKernelVersion("3"), PosixErrorIs(EINVAL, ::testing::_)); - ASSERT_THAT(ParseKernelVersion(""), PosixErrorIs(EINVAL, ::testing::_)); - ASSERT_THAT(ParseKernelVersion("version 3.3.10"), - PosixErrorIs(EINVAL, ::testing::_)); -} - -TEST(MatchersTest, SyscallSucceeds) { - EXPECT_THAT(0, SyscallSucceeds()); - EXPECT_THAT(0L, SyscallSucceeds()); - - errno = 0; - EXPECT_THAT(-1, SyscallSucceeds()); - EXPECT_THAT(-1L, SyscallSucceeds()); - - errno = ENOMEM; - EXPECT_THAT(-1, Not(SyscallSucceeds())); - EXPECT_THAT(-1L, Not(SyscallSucceeds())); -} - -TEST(MatchersTest, SyscallSucceedsWithValue) { - EXPECT_THAT(0, SyscallSucceedsWithValue(0)); - EXPECT_THAT(1, SyscallSucceedsWithValue(Lt(3))); - EXPECT_THAT(-1, Not(SyscallSucceedsWithValue(Lt(3)))); - EXPECT_THAT(4, Not(SyscallSucceedsWithValue(Lt(3)))); - - // Non-int -1 - EXPECT_THAT(-1L, Not(SyscallSucceedsWithValue(0))); - - // Non-int, truncates to -1 if converted to int, with expected value - EXPECT_THAT(0xffffffffL, SyscallSucceedsWithValue(0xffffffffL)); - - // Non-int, truncates to -1 if converted to int, with monomorphic matcher - EXPECT_THAT(0xffffffffL, - SyscallSucceedsWithValue(TypedEq<long>(0xffffffffL))); - - // Non-int, truncates to -1 if converted to int, with polymorphic matcher - EXPECT_THAT(0xffffffffL, SyscallSucceedsWithValue(Gt(1))); -} - -TEST(MatchersTest, SyscallFails) { - EXPECT_THAT(0, Not(SyscallFails())); - EXPECT_THAT(0L, Not(SyscallFails())); - - errno = 0; - EXPECT_THAT(-1, Not(SyscallFails())); - EXPECT_THAT(-1L, Not(SyscallFails())); - - errno = ENOMEM; - EXPECT_THAT(-1, SyscallFails()); - EXPECT_THAT(-1L, SyscallFails()); -} - -TEST(MatchersTest, SyscallFailsWithErrno) { - EXPECT_THAT(0, Not(SyscallFailsWithErrno(EINVAL))); - EXPECT_THAT(0L, Not(SyscallFailsWithErrno(EINVAL))); - - errno = ENOMEM; - EXPECT_THAT(-1, Not(SyscallFailsWithErrno(EINVAL))); - EXPECT_THAT(-1L, Not(SyscallFailsWithErrno(EINVAL))); - - errno = EINVAL; - EXPECT_THAT(-1, SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(-1L, SyscallFailsWithErrno(EINVAL)); - - EXPECT_THAT(-1, SyscallFailsWithErrno(AnyOf(EINVAL, ENOMEM))); - EXPECT_THAT(-1L, SyscallFailsWithErrno(AnyOf(EINVAL, ENOMEM))); - - std::vector<int> expected_errnos({EINVAL, ENOMEM}); - errno = ENOMEM; - EXPECT_THAT(-1, SyscallFailsWithErrno(ElementOf(expected_errnos))); - EXPECT_THAT(-1L, SyscallFailsWithErrno(ElementOf(expected_errnos))); -} - -TEST(AllBitwiseCombinationsTest, NoArguments) { - EXPECT_THAT(AllBitwiseCombinations(), IsEmpty()); -} - -TEST(AllBitwiseCombinationsTest, EmptyList) { - EXPECT_THAT(AllBitwiseCombinations(List<int>{}), IsEmpty()); -} - -TEST(AllBitwiseCombinationsTest, SingleElementList) { - EXPECT_THAT(AllBitwiseCombinations(List<int>{5}), UnorderedElementsAre(5)); -} - -TEST(AllBitwiseCombinationsTest, SingleList) { - EXPECT_THAT(AllBitwiseCombinations(List<int>{0, 1, 2, 4}), - UnorderedElementsAre(0, 1, 2, 4)); -} - -TEST(AllBitwiseCombinationsTest, MultipleLists) { - EXPECT_THAT( - AllBitwiseCombinations(List<int>{0, 1, 2, 3}, List<int>{0, 4, 8, 12}), - UnorderedElementsAreArray( - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15})); -} - -TEST(RandomizeBuffer, Works) { - const std::vector<char> original(4096); - std::vector<char> buffer = original; - RandomizeBuffer(buffer.data(), buffer.size()); - EXPECT_NE(buffer, original); -} - -// Enable comparison of vectors of iovec arrays for the following test. -MATCHER_P(IovecsListEq, expected, "") { - if (arg.size() != expected.size()) { - *result_listener << "sizes are different (actual: " << arg.size() - << ", expected: " << expected.size() << ")"; - return false; - } - - for (uint64_t i = 0; i < expected.size(); ++i) { - const std::vector<struct iovec>& actual_iovecs = arg[i]; - const std::vector<struct iovec>& expected_iovecs = expected[i]; - if (actual_iovecs.size() != expected_iovecs.size()) { - *result_listener << "iovec array size at position " << i - << " is different (actual: " << actual_iovecs.size() - << ", expected: " << expected_iovecs.size() << ")"; - return false; - } - - for (uint64_t j = 0; j < expected_iovecs.size(); ++j) { - const struct iovec& actual_iov = actual_iovecs[j]; - const struct iovec& expected_iov = expected_iovecs[j]; - if (actual_iov.iov_base != expected_iov.iov_base) { - *result_listener << "iovecs in array " << i << " at position " << j - << " are different (expected iov_base: " - << expected_iov.iov_base - << ", got: " << actual_iov.iov_base << ")"; - return false; - } - if (actual_iov.iov_len != expected_iov.iov_len) { - *result_listener << "iovecs in array " << i << " at position " << j - << " are different (expected iov_len: " - << expected_iov.iov_len - << ", got: " << actual_iov.iov_len << ")"; - return false; - } - } - } - - return true; -} - -// Verify empty iovec list generation. -TEST(GenerateIovecs, EmptyList) { - std::vector<char> buffer = {'a', 'b', 'c'}; - - EXPECT_THAT(GenerateIovecs(0, buffer.data(), buffer.size()), - IovecsListEq(std::vector<std::vector<struct iovec>>())); -} - -// Verify generating a single array of only one, partial, iovec. -TEST(GenerateIovecs, OneArray) { - std::vector<char> buffer = {'a', 'b', 'c'}; - - std::vector<std::vector<struct iovec>> expected; - struct iovec iov = {}; - iov.iov_base = buffer.data(); - iov.iov_len = 2; - expected.push_back(std::vector<struct iovec>({iov})); - EXPECT_THAT(GenerateIovecs(2, buffer.data(), buffer.size()), - IovecsListEq(expected)); -} - -// Verify that it wraps around after IOV_MAX iovecs. -TEST(GenerateIovecs, WrapsAtIovMax) { - std::vector<char> buffer = {'a', 'b', 'c'}; - - std::vector<std::vector<struct iovec>> expected; - struct iovec iov = {}; - iov.iov_base = buffer.data(); - iov.iov_len = buffer.size(); - expected.emplace_back(); - for (int i = 0; i < IOV_MAX; ++i) { - expected[0].push_back(iov); - } - iov.iov_len = 1; - expected.push_back(std::vector<struct iovec>({iov})); - - EXPECT_THAT( - GenerateIovecs(IOV_MAX * buffer.size() + 1, buffer.data(), buffer.size()), - IovecsListEq(expected)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/util/thread_util.h b/test/util/thread_util.h deleted file mode 100644 index 923c4fe10..000000000 --- a/test/util/thread_util.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_THREAD_UTIL_H_ -#define GVISOR_TEST_UTIL_THREAD_UTIL_H_ - -#include <pthread.h> -#ifdef __linux__ -#include <sys/syscall.h> -#endif -#include <unistd.h> - -#include <functional> -#include <utility> - -#include "test/util/logging.h" - -namespace gvisor { -namespace testing { - -// ScopedThread is a minimal wrapper around pthreads. -// -// This is used in lieu of more complex mechanisms because it provides very -// predictable behavior (no messing with timers, etc.) The thread will -// automatically joined when it is destructed (goes out of scope), but can be -// joined manually as well. -class ScopedThread { - public: - // Constructs a thread that executes f exactly once. - explicit ScopedThread(std::function<void*()> f) : f_(std::move(f)) { - CreateThread(); - } - - explicit ScopedThread(const std::function<void()>& f) { - f_ = [=] { - f(); - return nullptr; - }; - CreateThread(); - } - - ScopedThread(const ScopedThread& other) = delete; - ScopedThread& operator=(const ScopedThread& other) = delete; - - // Joins the thread. - ~ScopedThread() { Join(); } - - // Waits until this thread has finished executing. Join is idempotent and may - // be called multiple times, however Join itself is not thread-safe. - void* Join() { - if (!joined_) { - TEST_PCHECK(pthread_join(pt_, &retval_) == 0); - joined_ = true; - } - return retval_; - } - - private: - void CreateThread() { - TEST_PCHECK_MSG(pthread_create( - &pt_, /* attr = */ nullptr, - +[](void* arg) -> void* { - return static_cast<ScopedThread*>(arg)->f_(); - }, - this) == 0, - "thread creation failed"); - } - - std::function<void*()> f_; - pthread_t pt_; - bool joined_ = false; - void* retval_ = nullptr; -}; - -#ifdef __linux__ -inline pid_t gettid() { return syscall(SYS_gettid); } -#endif - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_THREAD_UTIL_H_ diff --git a/test/util/time_util.cc b/test/util/time_util.cc deleted file mode 100644 index 1ddfbfc9c..000000000 --- a/test/util/time_util.cc +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/time_util.h" - -#include <sys/syscall.h> -#include <unistd.h> - -#include "absl/time/time.h" - -namespace gvisor { -namespace testing { - -void SleepSafe(absl::Duration duration) { - if (duration == absl::ZeroDuration()) { - return; - } - - struct timespec ts = absl::ToTimespec(duration); - int ret; - while (1) { - ret = syscall(__NR_nanosleep, &ts, &ts); - if (ret == 0 || (ret <= 0 && errno != EINTR)) { - break; - } - } -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/time_util.h b/test/util/time_util.h deleted file mode 100644 index f3ddc9fde..000000000 --- a/test/util/time_util.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_TIME_UTIL_H_ -#define GVISOR_TEST_UTIL_TIME_UTIL_H_ - -#include "absl/time/time.h" - -namespace gvisor { -namespace testing { - -// Sleep for at least the specified duration. Avoids glibc. -void SleepSafe(absl::Duration duration); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_TIME_UTIL_H_ diff --git a/test/util/timer_util.cc b/test/util/timer_util.cc deleted file mode 100644 index 75cfc4f40..000000000 --- a/test/util/timer_util.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/timer_util.h" - -namespace gvisor { -namespace testing { - -absl::Time Now(clockid_t id) { - struct timespec now; - TEST_PCHECK(clock_gettime(id, &now) == 0); - return absl::TimeFromTimespec(now); -} - -#ifdef __linux__ - -PosixErrorOr<IntervalTimer> TimerCreate(clockid_t clockid, - const struct sigevent& sev) { - int timerid; - int ret = syscall(SYS_timer_create, clockid, &sev, &timerid); - if (ret < 0) { - return PosixError(errno, "timer_create"); - } - if (ret > 0) { - return PosixError(EINVAL, "timer_create should never return positive"); - } - MaybeSave(); - return IntervalTimer(timerid); -} - -#endif // __linux__ - -} // namespace testing -} // namespace gvisor diff --git a/test/util/timer_util.h b/test/util/timer_util.h deleted file mode 100644 index e389108ef..000000000 --- a/test/util/timer_util.h +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_UTIL_TIMER_UTIL_H_ -#define GVISOR_TEST_UTIL_TIMER_UTIL_H_ - -#include <errno.h> -#ifdef __linux__ -#include <sys/syscall.h> -#endif -#include <sys/time.h> - -#include <functional> - -#include "gmock/gmock.h" -#include "absl/time/time.h" -#include "test/util/cleanup.h" -#include "test/util/logging.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -// From Linux's include/uapi/asm-generic/siginfo.h. -#ifndef sigev_notify_thread_id -#define sigev_notify_thread_id _sigev_un._tid -#endif - -// Returns the current time. -absl::Time Now(clockid_t id); - -// MonotonicTimer is a simple timer that uses a monotonic clock. -class MonotonicTimer { - public: - MonotonicTimer() {} - absl::Duration Duration() { - struct timespec ts; - TEST_CHECK(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); - return absl::TimeFromTimespec(ts) - start_; - } - - void Start() { - struct timespec ts; - TEST_CHECK(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); - start_ = absl::TimeFromTimespec(ts); - } - - protected: - absl::Time start_; -}; - -// Sets the given itimer and returns a cleanup function that restores the -// previous itimer when it goes out of scope. -inline PosixErrorOr<Cleanup> ScopedItimer(int which, - struct itimerval const& new_value) { - struct itimerval old_value; - int rc = setitimer(which, &new_value, &old_value); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "setitimer failed"); - } - return Cleanup(std::function<void(void)>([which, old_value] { - EXPECT_THAT(setitimer(which, &old_value, nullptr), SyscallSucceeds()); - })); -} - -#ifdef __linux__ - -// RAII type for a kernel "POSIX" interval timer. (The kernel provides system -// calls such as timer_create that behave very similarly, but not identically, -// to those described by timer_create(2); in particular, the kernel does not -// implement SIGEV_THREAD. glibc builds POSIX-compliant interval timers based on -// these kernel interval timers.) -// -// Compare implementation to FileDescriptor. -class IntervalTimer { - public: - IntervalTimer() = default; - - explicit IntervalTimer(int id) { set_id(id); } - - IntervalTimer(IntervalTimer&& orig) : id_(orig.release()) {} - - IntervalTimer& operator=(IntervalTimer&& orig) { - if (this == &orig) return *this; - reset(orig.release()); - return *this; - } - - IntervalTimer(const IntervalTimer& other) = delete; - IntervalTimer& operator=(const IntervalTimer& other) = delete; - - ~IntervalTimer() { reset(); } - - int get() const { return id_; } - - int release() { - int const id = id_; - id_ = -1; - return id; - } - - void reset() { reset(-1); } - - void reset(int id) { - if (id_ >= 0) { - TEST_PCHECK(syscall(SYS_timer_delete, id_) == 0); - MaybeSave(); - } - set_id(id); - } - - PosixErrorOr<struct itimerspec> Set( - int flags, const struct itimerspec& new_value) const { - struct itimerspec old_value = {}; - if (syscall(SYS_timer_settime, id_, flags, &new_value, &old_value) < 0) { - return PosixError(errno, "timer_settime"); - } - MaybeSave(); - return old_value; - } - - PosixErrorOr<struct itimerspec> Get() const { - struct itimerspec curr_value = {}; - if (syscall(SYS_timer_gettime, id_, &curr_value) < 0) { - return PosixError(errno, "timer_gettime"); - } - MaybeSave(); - return curr_value; - } - - PosixErrorOr<int> Overruns() const { - int rv = syscall(SYS_timer_getoverrun, id_); - if (rv < 0) { - return PosixError(errno, "timer_getoverrun"); - } - MaybeSave(); - return rv; - } - - private: - void set_id(int id) { id_ = std::max(id, -1); } - - // Kernel timer_t is int; glibc timer_t is void*. - int id_ = -1; -}; - -// A wrapper around timer_create(2). -PosixErrorOr<IntervalTimer> TimerCreate(clockid_t clockid, - const struct sigevent& sev); - -#endif // __linux__ - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_TIMER_UTIL_H_ diff --git a/test/util/uid_util.cc b/test/util/uid_util.cc deleted file mode 100644 index b131b4b99..000000000 --- a/test/util/uid_util.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/util/posix_error.h" -#include "test/util/save_util.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr<bool> IsRoot() { - uid_t ruid, euid, suid; - int rc = getresuid(&ruid, &euid, &suid); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "getresuid"); - } - if (ruid != 0 || euid != 0 || suid != 0) { - return false; - } - gid_t rgid, egid, sgid; - rc = getresgid(&rgid, &egid, &sgid); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "getresgid"); - } - if (rgid != 0 || egid != 0 || sgid != 0) { - return false; - } - return true; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/uid_util.h b/test/util/uid_util.h deleted file mode 100644 index 2cd387fb0..000000000 --- a/test/util/uid_util.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_UID_UTIL_H_ -#define GVISOR_TEST_SYSCALLS_UID_UTIL_H_ - -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -// Returns true if the caller's real/effective/saved user/group IDs are all 0. -PosixErrorOr<bool> IsRoot(); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_UID_UTIL_H_ |