Files
Ayush Ranjan f06d4e7ebe goferfs: Add S/R support for open FDs to deleted files.
This support is only needed when the gofer mount in question is writable.
By default, the rootfs has an overlayfs applied, so the gofer lower layer is
not writabled. But if you are using --overlay2=none, then this change should
allow you to save sandbox with open FDs to deleted files in rootfs.

Updates #11425

PiperOrigin-RevId: 733021267
2025-03-03 12:38:10 -08:00

234 lines
7.1 KiB
C++

// 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 kIOUringEnabled[] = "IOURING_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 IsRunningOnRunsc() {
const char* env = getenv(kGvisorRuntime);
return env && strcmp(env, "runsc") == 0;
}
bool IsRunningWithHostinet() {
const char* env = getenv(kGvisorNetwork);
return env && strcmp(env, "host") == 0;
}
bool IsIOUringEnabled() {
const char* env = getenv(kIOUringEnabled);
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(char* buffer, size_t len) {
struct timespec ts = {};
clock_gettime(CLOCK_MONOTONIC, &ts);
uint32_t seed = static_cast<uint32_t>(ts.tv_nsec);
for (size_t i = 0; i < len; i++) {
buffer[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