// 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 #include #include #include #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 global_temp_file_number = ATOMIC_VAR_INIT(1); 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(); } // 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())); } PosixErrorOr 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::CreateDirWith(absl::string_view const parent, mode_t const mode) { return CreateIn(parent, [=](absl::string_view path) { return Mkdir(path, mode); }); } PosixErrorOr 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::CreateFileIn(absl::string_view const parent) { return TempPath::CreateFileWith(parent, absl::string_view(), kDefaultFileMode); } PosixErrorOr TempPath::CreateDirIn(absl::string_view const parent) { return TempPath::CreateDirWith(parent, kDefaultDirMode); } PosixErrorOr TempPath::CreateFileMode(mode_t mode) { return TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), absl::string_view(), mode); } PosixErrorOr TempPath::CreateFile() { return TempPath::CreateFileIn(GetAbsoluteTestTmpdir()); } PosixErrorOr 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