gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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(1);
    36  
    37  void TryDeleteRecursively(std::string const& path) {
    38    if (!path.empty()) {
    39      int undeleted_dirs = 0;
    40      int undeleted_files = 0;
    41      auto status = RecursivelyDelete(path, &undeleted_dirs, &undeleted_files);
    42      if (undeleted_dirs || undeleted_files || !status.ok()) {
    43        std::cerr << path << ": failed to delete " << undeleted_dirs
    44                  << " directories and " << undeleted_files
    45                  << " files: " << status << std::endl;
    46      }
    47    }
    48  }
    49  
    50  }  // namespace
    51  
    52  constexpr mode_t TempPath::kDefaultFileMode;
    53  constexpr mode_t TempPath::kDefaultDirMode;
    54  
    55  std::string NewTempAbsPathInDir(absl::string_view const dir) {
    56    return JoinPath(dir, NextTempBasename());
    57  }
    58  
    59  std::string NewTempAbsPath() {
    60    return NewTempAbsPathInDir(GetAbsoluteTestTmpdir());
    61  }
    62  
    63  std::string NewTempRelPath() { return NextTempBasename(); }
    64  
    65  std::string GetAbsoluteTestTmpdir() {
    66    // Note that TEST_TMPDIR is guaranteed to be set.
    67    char* env_tmpdir = getenv("TEST_TMPDIR");
    68    std::string tmp_dir =
    69        env_tmpdir != nullptr ? std::string(env_tmpdir) : "/tmp";
    70  
    71    return MakeAbsolute(tmp_dir, "").ValueOrDie();
    72  }
    73  
    74  // The global file number helps maintain file naming consistency across
    75  // different runs of a test.
    76  //
    77  // The timestamp is necessary because the test infrastructure invokes each
    78  // test case in a separate process (resetting global_temp_file_number) and
    79  // potentially in parallel, which allows for races between selecting and using a
    80  // name.
    81  std::string NextTempBasename() {
    82    return absl::StrCat("gvisor_test_temp_", global_temp_file_number++, "_",
    83                        absl::ToUnixNanos(absl::Now()));
    84  }
    85  
    86  PosixErrorOr<TempPath> TempPath::CreateFileWith(absl::string_view const parent,
    87                                                  absl::string_view const content,
    88                                                  mode_t const mode) {
    89    return CreateIn(parent, [=](absl::string_view path) -> PosixError {
    90      // CreateWithContents will call open(O_WRONLY) with the given mode. If the
    91      // mode is not user-writable, save/restore cannot preserve the fd. Hence
    92      // the little permission dance that's done here.
    93      auto res = CreateWithContents(path, content, mode | 0200);
    94      RETURN_IF_ERRNO(res);
    95  
    96      return Chmod(path, mode);
    97    });
    98  }
    99  
   100  PosixErrorOr<TempPath> TempPath::CreateDirWith(absl::string_view const parent,
   101                                                 mode_t const mode) {
   102    return CreateIn(parent,
   103                    [=](absl::string_view path) { return Mkdir(path, mode); });
   104  }
   105  
   106  PosixErrorOr<TempPath> TempPath::CreateSymlinkTo(absl::string_view const parent,
   107                                                   std::string const& dest) {
   108    return CreateIn(parent, [=](absl::string_view path) {
   109      int ret = symlink(dest.c_str(), std::string(path).c_str());
   110      if (ret != 0) {
   111        return PosixError(errno, "symlink failed");
   112      }
   113      return NoError();
   114    });
   115  }
   116  
   117  PosixErrorOr<TempPath> TempPath::CreateFileIn(absl::string_view const parent) {
   118    return TempPath::CreateFileWith(parent, absl::string_view(),
   119                                    kDefaultFileMode);
   120  }
   121  
   122  PosixErrorOr<TempPath> TempPath::CreateDirIn(absl::string_view const parent) {
   123    return TempPath::CreateDirWith(parent, kDefaultDirMode);
   124  }
   125  
   126  PosixErrorOr<TempPath> TempPath::CreateFileMode(mode_t mode) {
   127    return TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), absl::string_view(),
   128                                    mode);
   129  }
   130  
   131  PosixErrorOr<TempPath> TempPath::CreateFile() {
   132    return TempPath::CreateFileIn(GetAbsoluteTestTmpdir());
   133  }
   134  
   135  PosixErrorOr<TempPath> TempPath::CreateDir() {
   136    return TempPath::CreateDirIn(GetAbsoluteTestTmpdir());
   137  }
   138  
   139  TempPath::~TempPath() { TryDeleteRecursively(path_); }
   140  
   141  TempPath::TempPath(TempPath&& orig) { reset(orig.release()); }
   142  
   143  TempPath& TempPath::operator=(TempPath&& orig) {
   144    reset(orig.release());
   145    return *this;
   146  }
   147  
   148  std::string TempPath::reset(std::string newpath) {
   149    std::string path = path_;
   150    TryDeleteRecursively(path_);
   151    path_ = std::move(newpath);
   152    return path;
   153  }
   154  
   155  std::string TempPath::release() {
   156    std::string path = path_;
   157    path_ = std::string();
   158    return path;
   159  }
   160  
   161  }  // namespace testing
   162  }  // namespace gvisor