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