| // Copyright 2023 The BoringSSL 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 |
| // |
| // https://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 "file_util.h" |
| |
| #include <stdlib.h> |
| |
| #if defined(OPENSSL_WINDOWS) |
| OPENSSL_MSVC_PRAGMA(warning(push, 3)) |
| #include <windows.h> |
| OPENSSL_MSVC_PRAGMA(warning(pop)) |
| #else |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #endif |
| |
| #include <openssl/rand.h> |
| |
| #include "test_util.h" |
| |
| |
| BSSL_NAMESPACE_BEGIN |
| |
| #if defined(OPENSSL_WINDOWS) |
| static void PrintLastError(const char *s) { |
| DWORD error = GetLastError(); |
| char *buffer; |
| DWORD len = FormatMessageA( |
| FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 0, error, 0, |
| reinterpret_cast<char *>(&buffer), 0, nullptr); |
| std::string msg = "unknown error"; |
| if (len > 0) { |
| msg.assign(buffer, len); |
| while (!msg.empty() && (msg.back() == '\r' || msg.back() == '\n')) { |
| msg.resize(msg.size() - 1); |
| } |
| } |
| LocalFree(buffer); |
| fprintf(stderr, "%s: %s (0x%lx)\n", s, msg.c_str(), error); |
| } |
| #endif // OPENSSL_WINDOWS |
| |
| // GetTempDir returns the path to the temporary directory, or the empty string |
| // on error. On success, the result will include the directory separator. |
| static std::string GetTempDir() { |
| #if defined(OPENSSL_WINDOWS) |
| char buf[MAX_PATH + 1]; |
| DWORD len = GetTempPathA(sizeof(buf), buf); |
| return std::string(buf, len); |
| #else |
| const char *tmpdir = getenv("TMPDIR"); |
| if (tmpdir != nullptr && *tmpdir != '\0') { |
| std::string ret = tmpdir; |
| if (ret.back() != '/') { |
| ret.push_back('/'); |
| } |
| return ret; |
| } |
| #if defined(OPENSSL_ANDROID) |
| return "/data/local/tmp/"; |
| #else |
| return "/tmp/"; |
| #endif |
| #endif |
| } |
| |
| bool SkipTempFileTests() { |
| #if defined(OPENSSL_ANDROID) |
| // When running in an APK context, /data/local/tmp is unreadable. Android |
| // versions before https://android-review.googlesource.com/c/1821337 do not |
| // set TMPDIR to a suitable replacement. |
| if (getenv("TMPDIR") == nullptr) { |
| static bool should_skip = [] { |
| TemporaryFile file; |
| return !file.Init(); |
| }(); |
| if (should_skip) { |
| fprintf(stderr, "Skipping tests with temporary files.\n"); |
| return true; |
| } |
| } |
| #endif |
| return false; |
| } |
| |
| TemporaryFile::~TemporaryFile() { |
| #if defined(OPENSSL_WINDOWS) |
| if (!path_.empty() && !DeleteFileA(path_.c_str())) { |
| PrintLastError("Could not delete file"); |
| } |
| #else |
| if (!path_.empty() && unlink(path_.c_str()) != 0) { |
| perror("Could not delete file"); |
| } |
| #endif |
| } |
| |
| bool TemporaryFile::Init(bssl::Span<const uint8_t> content) { |
| std::string temp_dir = GetTempDir(); |
| if (temp_dir.empty()) { |
| return false; |
| } |
| |
| #if defined(OPENSSL_WINDOWS) |
| char path[MAX_PATH]; |
| if (GetTempFileNameA(temp_dir.c_str(), "bssl", |
| /*uUnique=*/0, path) == 0) { |
| PrintLastError("Could not create temporary"); |
| return false; |
| } |
| path_ = path; |
| #else |
| std::string path = temp_dir + "bssl_tmp_file.XXXXXX"; |
| int fd = mkstemp(path.data()); |
| if (fd < 0) { |
| perror("Could not create temporary file"); |
| return false; |
| } |
| close(fd); |
| path_ = std::move(path); |
| #endif |
| |
| ScopedFILE file = Open("wb"); |
| if (file == nullptr) { |
| perror("Could not open temporary file"); |
| return false; |
| } |
| if (!content.empty() && |
| fwrite(content.data(), content.size(), /*nitems=*/1, file.get()) != 1) { |
| perror("Could not write temporary file"); |
| return false; |
| } |
| return true; |
| } |
| |
| ScopedFILE TemporaryFile::Open(const char *mode) const { |
| if (path_.empty()) { |
| return nullptr; |
| } |
| return ScopedFILE(fopen(path_.c_str(), mode)); |
| } |
| |
| ScopedFD TemporaryFile::OpenFD(int flags) const { |
| if (path_.empty()) { |
| return ScopedFD(); |
| } |
| #if defined(OPENSSL_WINDOWS) |
| return ScopedFD(_open(path_.c_str(), flags)); |
| #else |
| return ScopedFD(open(path_.c_str(), flags)); |
| #endif |
| } |
| |
| TemporaryDirectory::~TemporaryDirectory() { |
| if (path_.empty()) { |
| return; |
| } |
| |
| #if defined(OPENSSL_WINDOWS) |
| for (const auto &file : files_) { |
| if (!DeleteFileA(GetFilePath(file).c_str())) { |
| PrintLastError("Could not delete file"); |
| } |
| } |
| if (!RemoveDirectoryA(path_.c_str())) { |
| PrintLastError("Could not delete directory"); |
| } |
| #else |
| for (const auto &file : files_) { |
| if (unlink(GetFilePath(file).c_str()) != 0) { |
| perror("Could not delete file"); |
| } |
| } |
| if (rmdir(path_.c_str()) != 0) { |
| perror("Could not delete directory"); |
| } |
| #endif |
| } |
| |
| bool TemporaryDirectory::Init() { |
| std::string path = GetTempDir(); |
| if (path.empty()) { |
| return false; |
| } |
| |
| #if defined(OPENSSL_WINDOWS) |
| // For simplicity, just try the first candidate and assume the directory |
| // doesn't exist. 128 bits of cryptographically secure randomness is plenty. |
| uint8_t buf[16]; |
| RAND_bytes(buf, sizeof(buf)); |
| path += "bssl_tmp_dir." + EncodeHex(buf); |
| if (!CreateDirectoryA(path.c_str(), /*lpSecurityAttributes=*/nullptr)) { |
| perror("Could not make temporary directory"); |
| return false; |
| } |
| #else |
| path += "bssl_tmp_dir.XXXXXX"; |
| if (mkdtemp(path.data()) == nullptr) { |
| perror("Could not make temporary directory"); |
| return false; |
| } |
| #endif |
| |
| path_ = std::move(path); |
| return true; |
| } |
| |
| bool TemporaryDirectory::AddFile(const std::string &filename, |
| bssl::Span<const uint8_t> content) { |
| if (path_.empty()) { |
| return false; |
| } |
| |
| ScopedFILE file(fopen(GetFilePath(filename).c_str(), "wb")); |
| if (file == nullptr) { |
| perror("Could not open temporary file"); |
| return false; |
| } |
| if (!content.empty() && |
| fwrite(content.data(), content.size(), /*nitems=*/1, file.get()) != 1) { |
| perror("Could not write temporary file"); |
| return false; |
| } |
| |
| files_.insert(filename); |
| return true; |
| } |
| |
| BSSL_NAMESPACE_END |