mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
ae1294b435
PiperOrigin-RevId: 567370524
233 lines
7.7 KiB
C++
233 lines
7.7 KiB
C++
// 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 <cerrno>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <vector>
|
|
|
|
#include "absl/container/flat_hash_map.h"
|
|
#include "absl/strings/numbers.h"
|
|
#include "absl/strings/str_join.h"
|
|
#include "absl/strings/str_split.h"
|
|
#include "absl/types/span.h"
|
|
#include "test/util/posix_error.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() > 13) {
|
|
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 and can have up
|
|
// to 3 elements. We know based on the total number of tokens.
|
|
int off = -1;
|
|
if (fields.size() > 10) {
|
|
int num_optional_tags = fields.size() - 10;
|
|
entry.optional = absl::StrJoin(
|
|
absl::MakeSpan(fields).subspan(6, num_optional_tags), " ");
|
|
off += num_optional_tags;
|
|
}
|
|
// 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;
|
|
}
|
|
|
|
PosixErrorOr<absl::flat_hash_map<std::string, std::vector<MountOptional>>>
|
|
MountOptionals() {
|
|
absl::flat_hash_map<std::string, std::vector<MountOptional>> optionals;
|
|
ASSIGN_OR_RETURN_ERRNO(std::vector<ProcMountInfoEntry> mounts,
|
|
ProcSelfMountInfoEntries());
|
|
for (const auto& e : mounts) {
|
|
MountOptional opt;
|
|
opt.shared = 0;
|
|
opt.master = 0;
|
|
opt.propagate_from = 0;
|
|
std::vector<std::string_view> tags = absl::StrSplit(e.optional, ' ');
|
|
|
|
for (std::string_view tag : tags) {
|
|
PosixError err = ParseOptionalTag(tag, &opt);
|
|
if (!err.ok()) return err;
|
|
}
|
|
if (optionals.contains(e.mount_point)) {
|
|
optionals[e.mount_point].push_back(opt);
|
|
} else {
|
|
optionals[e.mount_point] = {opt};
|
|
}
|
|
}
|
|
return optionals;
|
|
}
|
|
|
|
PosixError ParseOptionalTag(std::string_view tag, MountOptional* opt) {
|
|
std::vector<absl::string_view> key_value =
|
|
absl::StrSplit(absl::string_view(tag.data(), tag.size()), ':');
|
|
if (key_value.size() != 2) return PosixError(0);
|
|
if (key_value[0] == "shared") {
|
|
if (!absl::SimpleAtoi(key_value[1], &opt->shared))
|
|
return PosixError(EINVAL,
|
|
"could not parse shared value in optional string");
|
|
} else if (key_value[0] == "master") {
|
|
if (!absl::SimpleAtoi(key_value[1], &opt->master))
|
|
return PosixError(EINVAL,
|
|
"could not parse master value in optional string");
|
|
} else if (key_value[0] == "propagate_from") {
|
|
if (!absl::SimpleAtoi(key_value[1], &opt->propagate_from))
|
|
return PosixError(EINVAL,
|
|
"could not parse propagate_from in optional string");
|
|
}
|
|
return PosixError(0);
|
|
}
|
|
|
|
} // namespace testing
|
|
} // namespace gvisor
|