github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/util/temp_path.cc (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "test/util/temp_path.h" 16 17 #include <unistd.h> 18 19 #include <atomic> 20 #include <cstdlib> 21 #include <iostream> 22 23 #include "gtest/gtest.h" 24 #include "absl/time/clock.h" 25 #include "absl/time/time.h" 26 #include "test/util/fs_util.h" 27 #include "test/util/posix_error.h" 28 #include "test/util/test_util.h" 29 30 namespace gvisor { 31 namespace testing { 32 33 namespace { 34 35 std::atomic<uint64_t> global_temp_file_number = ATOMIC_VAR_INIT(1); 36 37 // Return a new temp filename, intended to be unique system-wide. 38 // 39 // The global file number helps maintain file naming consistency across 40 // different runs of a test. 41 // 42 // The timestamp is necessary because the test infrastructure invokes each 43 // test case in a separate process (resetting global_temp_file_number) and 44 // potentially in parallel, which allows for races between selecting and using a 45 // name. 46 std::string NextTempBasename() { 47 return absl::StrCat("gvisor_test_temp_", global_temp_file_number++, "_", 48 absl::ToUnixNanos(absl::Now())); 49 } 50 51 void TryDeleteRecursively(std::string const& path) { 52 if (!path.empty()) { 53 int undeleted_dirs = 0; 54 int undeleted_files = 0; 55 auto status = RecursivelyDelete(path, &undeleted_dirs, &undeleted_files); 56 if (undeleted_dirs || undeleted_files || !status.ok()) { 57 std::cerr << path << ": failed to delete " << undeleted_dirs 58 << " directories and " << undeleted_files 59 << " files: " << status << std::endl; 60 } 61 } 62 } 63 64 } // namespace 65 66 constexpr mode_t TempPath::kDefaultFileMode; 67 constexpr mode_t TempPath::kDefaultDirMode; 68 69 std::string NewTempAbsPathInDir(absl::string_view const dir) { 70 return JoinPath(dir, NextTempBasename()); 71 } 72 73 std::string NewTempAbsPath() { 74 return NewTempAbsPathInDir(GetAbsoluteTestTmpdir()); 75 } 76 77 std::string NewTempRelPath() { return NextTempBasename(); } 78 79 std::string GetAbsoluteTestTmpdir() { 80 // Note that TEST_TMPDIR is guaranteed to be set. 81 char* env_tmpdir = getenv("TEST_TMPDIR"); 82 std::string tmp_dir = 83 env_tmpdir != nullptr ? std::string(env_tmpdir) : "/tmp"; 84 85 return MakeAbsolute(tmp_dir, "").ValueOrDie(); 86 } 87 88 PosixErrorOr<TempPath> TempPath::CreateFileWith(absl::string_view const parent, 89 absl::string_view const content, 90 mode_t const mode) { 91 return CreateIn(parent, [=](absl::string_view path) -> PosixError { 92 // CreateWithContents will call open(O_WRONLY) with the given mode. If the 93 // mode is not user-writable, save/restore cannot preserve the fd. Hence 94 // the little permission dance that's done here. 95 auto res = CreateWithContents(path, content, mode | 0200); 96 RETURN_IF_ERRNO(res); 97 98 return Chmod(path, mode); 99 }); 100 } 101 102 PosixErrorOr<TempPath> TempPath::CreateDirWith(absl::string_view const parent, 103 mode_t const mode) { 104 return CreateIn(parent, 105 [=](absl::string_view path) { return Mkdir(path, mode); }); 106 } 107 108 PosixErrorOr<TempPath> TempPath::CreateSymlinkTo(absl::string_view const parent, 109 std::string const& dest) { 110 return CreateIn(parent, [=](absl::string_view path) { 111 int ret = symlink(dest.c_str(), std::string(path).c_str()); 112 if (ret != 0) { 113 return PosixError(errno, "symlink failed"); 114 } 115 return NoError(); 116 }); 117 } 118 119 PosixErrorOr<TempPath> TempPath::CreateFileIn(absl::string_view const parent) { 120 return TempPath::CreateFileWith(parent, absl::string_view(), 121 kDefaultFileMode); 122 } 123 124 PosixErrorOr<TempPath> TempPath::CreateDirIn(absl::string_view const parent) { 125 return TempPath::CreateDirWith(parent, kDefaultDirMode); 126 } 127 128 PosixErrorOr<TempPath> TempPath::CreateFileMode(mode_t mode) { 129 return TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), absl::string_view(), 130 mode); 131 } 132 133 PosixErrorOr<TempPath> TempPath::CreateFile() { 134 return TempPath::CreateFileIn(GetAbsoluteTestTmpdir()); 135 } 136 137 PosixErrorOr<TempPath> TempPath::CreateDir() { 138 return TempPath::CreateDirIn(GetAbsoluteTestTmpdir()); 139 } 140 141 TempPath::~TempPath() { TryDeleteRecursively(path_); } 142 143 TempPath::TempPath(TempPath&& orig) { reset(orig.release()); } 144 145 TempPath& TempPath::operator=(TempPath&& orig) { 146 reset(orig.release()); 147 return *this; 148 } 149 150 std::string TempPath::reset(std::string newpath) { 151 std::string path = path_; 152 TryDeleteRecursively(path_); 153 path_ = std::move(newpath); 154 return path; 155 } 156 157 std::string TempPath::release() { 158 std::string path = path_; 159 path_ = std::string(); 160 return path; 161 } 162 163 } // namespace testing 164 } // namespace gvisor