xref: /aosp_15_r20/external/boringssl/src/crypto/test/file_util.cc (revision 8fb009dc861624b67b6cdb62ea21f0f22d0c584b)
1 /* Copyright (c) 2023, Google Inc.
2  *
3  * Permission to use, copy, modify, and/or distribute this software for any
4  * purpose with or without fee is hereby granted, provided that the above
5  * copyright notice and this permission notice appear in all copies.
6  *
7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14 
15 #include "file_util.h"
16 
17 #include <stdlib.h>
18 
19 #if defined(OPENSSL_WINDOWS)
20 OPENSSL_MSVC_PRAGMA(warning(push, 3))
21 #include <windows.h>
OPENSSL_MSVC_PRAGMA(warning (pop))22 OPENSSL_MSVC_PRAGMA(warning(pop))
23 #else
24 #include <fcntl.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27 #endif
28 
29 #include <openssl/rand.h>
30 
31 #include "test_util.h"
32 
33 
34 BSSL_NAMESPACE_BEGIN
35 
36 #if defined(OPENSSL_WINDOWS)
37 static void PrintLastError(const char *s) {
38   DWORD error = GetLastError();
39   char *buffer;
40   DWORD len = FormatMessageA(
41       FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 0, error, 0,
42       reinterpret_cast<char *>(&buffer), 0, nullptr);
43   std::string msg = "unknown error";
44   if (len > 0) {
45     msg.assign(buffer, len);
46     while (!msg.empty() && (msg.back() == '\r' || msg.back() == '\n')) {
47       msg.resize(msg.size() - 1);
48     }
49   }
50   LocalFree(buffer);
51   fprintf(stderr, "%s: %s (0x%lx)\n", s, msg.c_str(), error);
52 }
53 #endif  // OPENSSL_WINDOWS
54 
55 // GetTempDir returns the path to the temporary directory, or the empty string
56 // on error. On success, the result will include the directory separator.
GetTempDir()57 static std::string GetTempDir() {
58 #if defined(OPENSSL_WINDOWS)
59   char buf[MAX_PATH + 1];
60   DWORD len = GetTempPathA(sizeof(buf), buf);
61   return std::string(buf, len);
62 #else
63   const char *tmpdir = getenv("TMPDIR");
64   if (tmpdir != nullptr && *tmpdir != '\0') {
65     std::string ret = tmpdir;
66     if (ret.back() != '/') {
67       ret.push_back('/');
68     }
69     return ret;
70   }
71 #if defined(OPENSSL_ANDROID)
72   return "/data/local/tmp/";
73 #else
74   return "/tmp/";
75 #endif
76 #endif
77 }
78 
SkipTempFileTests()79 bool SkipTempFileTests() {
80 #if defined(OPENSSL_ANDROID)
81   // When running in an APK context, /data/local/tmp is unreadable. Android
82   // versions before https://android-review.googlesource.com/c/1821337 do not
83   // set TMPDIR to a suitable replacement.
84   if (getenv("TMPDIR") == nullptr) {
85     static bool should_skip = [] {
86       TemporaryFile file;
87       return !file.Init();
88     }();
89     if (should_skip) {
90       fprintf(stderr, "Skipping tests with temporary files.\n");
91       return true;
92     }
93   }
94 #endif
95   return false;
96 }
97 
~TemporaryFile()98 TemporaryFile::~TemporaryFile() {
99 #if defined(OPENSSL_WINDOWS)
100   if (!path_.empty() && !DeleteFileA(path_.c_str())) {
101     PrintLastError("Could not delete file");
102   }
103 #else
104   if (!path_.empty() && unlink(path_.c_str()) != 0) {
105     perror("Could not delete file");
106   }
107 #endif
108 }
109 
Init(bssl::Span<const uint8_t> content)110 bool TemporaryFile::Init(bssl::Span<const uint8_t> content) {
111   std::string temp_dir = GetTempDir();
112   if (temp_dir.empty()) {
113     return false;
114   }
115 
116 #if defined(OPENSSL_WINDOWS)
117   char path[MAX_PATH];
118   if (GetTempFileNameA(temp_dir.c_str(), "bssl",
119                        /*uUnique=*/0, path) == 0) {
120     PrintLastError("Could not create temporary");
121     return false;
122   }
123   path_ = path;
124 #else
125   std::string path = temp_dir + "bssl_tmp_file.XXXXXX";
126   // TODO(davidben): Use |path.data()| when we require C++17.
127   int fd = mkstemp(&path[0]);
128   if (fd < 0) {
129     perror("Could not create temporary file");
130     return false;
131   }
132   close(fd);
133   path_ = std::move(path);
134 #endif
135 
136   ScopedFILE file = Open("wb");
137   if (file == nullptr) {
138     perror("Could not open temporary file");
139     return false;
140   }
141   if (!content.empty() &&
142       fwrite(content.data(), content.size(), /*nitems=*/1, file.get()) != 1) {
143     perror("Could not write temporary file");
144     return false;
145   }
146   return true;
147 }
148 
Open(const char * mode) const149 ScopedFILE TemporaryFile::Open(const char *mode) const {
150   if (path_.empty()) {
151     return nullptr;
152   }
153   return ScopedFILE(fopen(path_.c_str(), mode));
154 }
155 
OpenFD(int flags) const156 ScopedFD TemporaryFile::OpenFD(int flags) const {
157   if (path_.empty()) {
158     return ScopedFD();
159   }
160 #if defined(OPENSSL_WINDOWS)
161   return ScopedFD(_open(path_.c_str(), flags));
162 #else
163   return ScopedFD(open(path_.c_str(), flags));
164 #endif
165 }
166 
~TemporaryDirectory()167 TemporaryDirectory::~TemporaryDirectory() {
168   if (path_.empty()) {
169     return;
170   }
171 
172 #if defined(OPENSSL_WINDOWS)
173   for (const auto &file : files_) {
174     if (!DeleteFileA(GetFilePath(file).c_str())) {
175       PrintLastError("Could not delete file");
176     }
177   }
178   if (!RemoveDirectoryA(path_.c_str())) {
179     PrintLastError("Could not delete directory");
180   }
181 #else
182   for (const auto &file : files_) {
183     if (unlink(GetFilePath(file).c_str()) != 0) {
184       perror("Could not delete file");
185     }
186   }
187   if (rmdir(path_.c_str()) != 0) {
188     perror("Could not delete directory");
189   }
190 #endif
191 }
192 
Init()193 bool TemporaryDirectory::Init() {
194   std::string path = GetTempDir();
195   if (path.empty()) {
196     return false;
197   }
198 
199 #if defined(OPENSSL_WINDOWS)
200   // For simplicity, just try the first candidate and assume the directory
201   // doesn't exist. 128 bits of cryptographically secure randomness is plenty.
202   uint8_t buf[16];
203   RAND_bytes(buf, sizeof(buf));
204   path += "bssl_tmp_dir." + EncodeHex(buf);
205   if (!CreateDirectoryA(path.c_str(), /*lpSecurityAttributes=*/nullptr)) {
206     perror("Could not make temporary directory");
207     return false;
208   }
209 #else
210   path += "bssl_tmp_dir.XXXXXX";
211   // TODO(davidben): Use |path.data()| when we require C++17.
212   if (mkdtemp(&path[0]) == nullptr) {
213     perror("Could not make temporary directory");
214     return false;
215   }
216 #endif
217 
218   path_ = std::move(path);
219   return true;
220 }
221 
AddFile(const std::string & filename,bssl::Span<const uint8_t> content)222 bool TemporaryDirectory::AddFile(const std::string &filename,
223                                  bssl::Span<const uint8_t> content) {
224   if (path_.empty()) {
225     return false;
226   }
227 
228   ScopedFILE file(fopen(GetFilePath(filename).c_str(), "wb"));
229   if (file == nullptr) {
230     perror("Could not open temporary file");
231     return false;
232   }
233   if (!content.empty() &&
234       fwrite(content.data(), content.size(), /*nitems=*/1, file.get()) != 1) {
235     perror("Could not write temporary file");
236     return false;
237   }
238 
239   files_.insert(filename);
240   return true;
241 }
242 
243 BSSL_NAMESPACE_END
244