diff options
Diffstat (limited to 'test/util')
-rw-r--r-- | test/util/BUILD | 3 | ||||
-rw-r--r-- | test/util/cgroup_util.cc | 2 | ||||
-rw-r--r-- | test/util/cgroup_util.h | 4 | ||||
-rw-r--r-- | test/util/mount_util.cc | 166 | ||||
-rw-r--r-- | test/util/mount_util.h | 38 |
5 files changed, 211 insertions, 2 deletions
diff --git a/test/util/BUILD b/test/util/BUILD index 383de00ed..6feda0e26 100644 --- a/test/util/BUILD +++ b/test/util/BUILD @@ -137,11 +137,14 @@ cc_library( 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, ], ) diff --git a/test/util/cgroup_util.cc b/test/util/cgroup_util.cc index d8d3fe471..04d4f8de0 100644 --- a/test/util/cgroup_util.cc +++ b/test/util/cgroup_util.cc @@ -25,7 +25,7 @@ namespace gvisor { namespace testing { -Cgroup::Cgroup(std::string path) : cgroup_path_(path) { +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; diff --git a/test/util/cgroup_util.h b/test/util/cgroup_util.h index c6e4303e1..b797a8b24 100644 --- a/test/util/cgroup_util.h +++ b/test/util/cgroup_util.h @@ -30,10 +30,12 @@ namespace testing { // Cgroup represents a cgroup directory on a mounted cgroupfs. class Cgroup { public: - Cgroup(std::string path); + 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); } diff --git a/test/util/mount_util.cc b/test/util/mount_util.cc new file mode 100644 index 000000000..a79ce6420 --- /dev/null +++ b/test/util/mount_util.cc @@ -0,0 +1,166 @@ +// 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)); + + std::vector<ProcMountsEntry> entries; + std::vector<std::string> lines = absl::StrSplit(content, '\n'); + 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::SkipEmpty()); + if (fields.size() != 6) { + return PosixError(EINVAL, + absl::StrFormat("Not enough tokens, got %d, line: %s", + fields.size(), line)); + } + + 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)); + + std::vector<ProcMountInfoEntry> entries; + std::vector<std::string> lines = absl::StrSplit(content, '\n'); + 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::SkipEmpty()); + if (fields.size() < 10 || fields.size() > 11) { + return PosixError( + EINVAL, + absl::StrFormat("Unexpected number of tokens, got %d, line: %s", + fields.size(), line)); + } + + 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::SkipEmpty()); + 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 index 09e2281eb..b75a490fb 100644 --- a/test/util/mount_util.h +++ b/test/util/mount_util.h @@ -22,6 +22,7 @@ #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" @@ -45,6 +46,43 @@ inline PosixErrorOr<Cleanup> Mount(const std::string& source, }); } +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(); + +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(); + +// 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 |