summaryrefslogtreecommitdiffhomepage
path: root/test/util/fs_util.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/util/fs_util.cc')
-rw-r--r--test/util/fs_util.cc736
1 files changed, 0 insertions, 736 deletions
diff --git a/test/util/fs_util.cc b/test/util/fs_util.cc
deleted file mode 100644
index 1c24d9ffc..000000000
--- a/test/util/fs_util.cc
+++ /dev/null
@@ -1,736 +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 Unlink(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 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::StrFormat("mkdir \"%s\" mode %#o", path, 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