1 // Copyright 2023 The BoringSSL 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 // https://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 #ifndef OPENSSL_HEADER_CRYPTO_TEST_FILE_UTIL_H 16 #define OPENSSL_HEADER_CRYPTO_TEST_FILE_UTIL_H 17 18 #include <stdio.h> 19 20 #include <memory> 21 #include <set> 22 #include <string> 23 #include <string_view> 24 #include <utility> 25 26 #include <openssl/span.h> 27 28 #if defined(OPENSSL_WINDOWS) 29 #include <io.h> 30 #else 31 #include <unistd.h> 32 #endif 33 34 35 BSSL_NAMESPACE_BEGIN 36 37 struct FileDeleter { operatorFileDeleter38 void operator()(FILE *f) const { 39 if (f != nullptr) { 40 fclose(f); 41 } 42 } 43 }; 44 45 using ScopedFILE = std::unique_ptr<FILE, FileDeleter>; 46 47 class ScopedFD { 48 public: 49 ScopedFD() = default; ScopedFD(int fd)50 explicit ScopedFD(int fd) : fd_(fd) {} ~ScopedFD()51 ~ScopedFD() { reset(); } 52 ScopedFD(ScopedFD && other)53 ScopedFD(ScopedFD &&other) { *this = std::move(other); } 54 ScopedFD &operator=(ScopedFD other) { 55 reset(other.release()); 56 return *this; 57 } 58 is_valid()59 bool is_valid() const { return fd_ >= 0; } get()60 int get() const { return fd_; } 61 release()62 int release() { return std::exchange(fd_, -1); } 63 void reset(int fd = -1) { 64 if (is_valid()) { 65 #if defined(OPENSSL_WINDOWS) 66 _close(fd_); 67 #else 68 close(fd_); 69 #endif 70 } 71 fd_ = fd; 72 } 73 74 private: 75 int fd_ = -1; 76 }; 77 78 // SkipTempFileTests returns true and prints a warning if tests involving 79 // temporary files should be skipped because of platform issues. 80 bool SkipTempFileTests(); 81 82 // TemporaryFile manages a temporary file for testing. 83 class TemporaryFile { 84 public: 85 TemporaryFile() = default; 86 ~TemporaryFile(); 87 TemporaryFile(TemporaryFile & other)88 TemporaryFile(TemporaryFile &other) { *this = std::move(other); } 89 TemporaryFile& operator=(TemporaryFile&&other) { 90 // Ensure |path_| is empty so it doesn't try to delete the File. 91 path_ = std::exchange(other.path_, {}); 92 return *this; 93 } 94 95 // Init initializes the temporary file with the specified content. It returns 96 // true on success and false on error. On error, callers should call 97 // |IgnoreTempFileErrors| to determine whether to ignore the error. 98 bool Init(bssl::Span<const uint8_t> content = {}); Init(std::string_view content)99 bool Init(std::string_view content) { 100 return Init(bssl::StringAsBytes(content)); 101 } 102 103 // Open opens the file as a |FILE| with the specified mode. 104 ScopedFILE Open(const char *mode) const; 105 106 // Open opens the file as a file descriptor with the specified flags. 107 ScopedFD OpenFD(int flags) const; 108 109 // path returns the path to the temporary file. path()110 const std::string &path() const { return path_; } 111 112 private: 113 std::string path_; 114 }; 115 116 // TemporaryDirectory manages a temporary directory for testing. 117 class TemporaryDirectory { 118 public: 119 TemporaryDirectory() = default; 120 ~TemporaryDirectory(); 121 TemporaryDirectory(TemporaryDirectory & other)122 TemporaryDirectory(TemporaryDirectory &other) { *this = std::move(other); } 123 TemporaryDirectory& operator=(TemporaryDirectory&&other) { 124 // Ensure |other_| is empty so it doesn't try to delete the directory. 125 path_ = std::exchange(other.path_, {}); 126 files_ = std::exchange(other.files_, {}); 127 return *this; 128 } 129 130 // Init initializes the temporary directory. It returns true on success and 131 // false on error. On error, callers should call |IgnoreTempFileErrors| to 132 // determine whether to ignore the error. 133 bool Init(); 134 135 // path returns the path to the temporary directory. path()136 const std::string &path() const { return path_; } 137 138 // AddFile adds a file to the temporary directory with the specified content. 139 // It returns true on success and false on error. Subdirectories in the 140 // temporary directory are not currently supported. 141 bool AddFile(const std::string &filename, bssl::Span<const uint8_t> content); AddFile(const std::string & filename,std::string_view content)142 bool AddFile(const std::string &filename, std::string_view content) { 143 return AddFile(filename, bssl::StringAsBytes(content)); 144 } 145 146 // GetFilePath returns the path to the specified file within the temporary 147 // directory. GetFilePath(const std::string & filename)148 std::string GetFilePath(const std::string &filename) { 149 #if defined(OPENSSL_WINDOWS) 150 return path_ + '\\' + filename; 151 #else 152 return path_ + '/' + filename; 153 #endif 154 } 155 156 private: 157 std::string path_; 158 std::set<std::string> files_; 159 }; 160 161 BSSL_NAMESPACE_END 162 163 #endif // OPENSSL_HEADER_CRYPTO_TEST_FILE_UTIL_H 164