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 #include "file_util.h"
16
17 #include <stdlib.h>
18
19 #if defined(OPENSSL_WINDOWS)
20 #include <windows.h>
21 #else
22 #include <fcntl.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #endif
26
27 #include <openssl/rand.h>
28
29 #include "test_util.h"
30
31
32 BSSL_NAMESPACE_BEGIN
33
34 #if defined(OPENSSL_WINDOWS)
PrintLastError(const char * s)35 static void PrintLastError(const char *s) {
36 DWORD error = GetLastError();
37 char *buffer;
38 DWORD len = FormatMessageA(
39 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 0, error, 0,
40 reinterpret_cast<char *>(&buffer), 0, nullptr);
41 std::string msg = "unknown error";
42 if (len > 0) {
43 msg.assign(buffer, len);
44 while (!msg.empty() && (msg.back() == '\r' || msg.back() == '\n')) {
45 msg.resize(msg.size() - 1);
46 }
47 }
48 LocalFree(buffer);
49 fprintf(stderr, "%s: %s (0x%lx)\n", s, msg.c_str(), error);
50 }
51 #endif // OPENSSL_WINDOWS
52
53 // GetTempDir returns the path to the temporary directory, or the empty string
54 // on error. On success, the result will include the directory separator.
GetTempDir()55 static std::string GetTempDir() {
56 #if defined(OPENSSL_WINDOWS)
57 char buf[MAX_PATH + 1];
58 DWORD len = GetTempPathA(sizeof(buf), buf);
59 return std::string(buf, len);
60 #else
61 const char *tmpdir = getenv("TMPDIR");
62 if (tmpdir != nullptr && *tmpdir != '\0') {
63 std::string ret = tmpdir;
64 if (ret.back() != '/') {
65 ret.push_back('/');
66 }
67 return ret;
68 }
69 #if defined(OPENSSL_ANDROID)
70 return "/data/local/tmp/";
71 #else
72 return "/tmp/";
73 #endif
74 #endif
75 }
76
SkipTempFileTests()77 bool SkipTempFileTests() {
78 #if defined(OPENSSL_ANDROID)
79 // When running in an APK context, /data/local/tmp is unreadable. Android
80 // versions before https://android-review.googlesource.com/c/1821337 do not
81 // set TMPDIR to a suitable replacement.
82 if (getenv("TMPDIR") == nullptr) {
83 static bool should_skip = [] {
84 TemporaryFile file;
85 return !file.Init();
86 }();
87 if (should_skip) {
88 fprintf(stderr, "Skipping tests with temporary files.\n");
89 return true;
90 }
91 }
92 #endif
93 return false;
94 }
95
~TemporaryFile()96 TemporaryFile::~TemporaryFile() {
97 #if defined(OPENSSL_WINDOWS)
98 if (!path_.empty() && !DeleteFileA(path_.c_str())) {
99 PrintLastError("Could not delete file");
100 }
101 #else
102 if (!path_.empty() && unlink(path_.c_str()) != 0) {
103 perror("Could not delete file");
104 }
105 #endif
106 }
107
Init(bssl::Span<const uint8_t> content)108 bool TemporaryFile::Init(bssl::Span<const uint8_t> content) {
109 std::string temp_dir = GetTempDir();
110 if (temp_dir.empty()) {
111 return false;
112 }
113
114 #if defined(OPENSSL_WINDOWS)
115 char path[MAX_PATH];
116 if (GetTempFileNameA(temp_dir.c_str(), "bssl",
117 /*uUnique=*/0, path) == 0) {
118 PrintLastError("Could not create temporary");
119 return false;
120 }
121 path_ = path;
122 #else
123 std::string path = temp_dir + "bssl_tmp_file.XXXXXX";
124 int fd = mkstemp(path.data());
125 if (fd < 0) {
126 perror("Could not create temporary file");
127 return false;
128 }
129 close(fd);
130 path_ = std::move(path);
131 #endif
132
133 ScopedFILE file = Open("wb");
134 if (file == nullptr) {
135 perror("Could not open temporary file");
136 return false;
137 }
138 if (!content.empty() &&
139 fwrite(content.data(), content.size(), /*nitems=*/1, file.get()) != 1) {
140 perror("Could not write temporary file");
141 return false;
142 }
143 return true;
144 }
145
Open(const char * mode) const146 ScopedFILE TemporaryFile::Open(const char *mode) const {
147 if (path_.empty()) {
148 return nullptr;
149 }
150 return ScopedFILE(fopen(path_.c_str(), mode));
151 }
152
OpenFD(int flags) const153 ScopedFD TemporaryFile::OpenFD(int flags) const {
154 if (path_.empty()) {
155 return ScopedFD();
156 }
157 #if defined(OPENSSL_WINDOWS)
158 return ScopedFD(_open(path_.c_str(), flags));
159 #else
160 return ScopedFD(open(path_.c_str(), flags));
161 #endif
162 }
163
~TemporaryDirectory()164 TemporaryDirectory::~TemporaryDirectory() {
165 if (path_.empty()) {
166 return;
167 }
168
169 #if defined(OPENSSL_WINDOWS)
170 for (const auto &file : files_) {
171 if (!DeleteFileA(GetFilePath(file).c_str())) {
172 PrintLastError("Could not delete file");
173 }
174 }
175 if (!RemoveDirectoryA(path_.c_str())) {
176 PrintLastError("Could not delete directory");
177 }
178 #else
179 for (const auto &file : files_) {
180 if (unlink(GetFilePath(file).c_str()) != 0) {
181 perror("Could not delete file");
182 }
183 }
184 if (rmdir(path_.c_str()) != 0) {
185 perror("Could not delete directory");
186 }
187 #endif
188 }
189
Init()190 bool TemporaryDirectory::Init() {
191 std::string path = GetTempDir();
192 if (path.empty()) {
193 return false;
194 }
195
196 #if defined(OPENSSL_WINDOWS)
197 // For simplicity, just try the first candidate and assume the directory
198 // doesn't exist. 128 bits of cryptographically secure randomness is plenty.
199 uint8_t buf[16];
200 RAND_bytes(buf, sizeof(buf));
201 path += "bssl_tmp_dir." + EncodeHex(buf);
202 if (!CreateDirectoryA(path.c_str(), /*lpSecurityAttributes=*/nullptr)) {
203 perror("Could not make temporary directory");
204 return false;
205 }
206 #else
207 path += "bssl_tmp_dir.XXXXXX";
208 if (mkdtemp(path.data()) == nullptr) {
209 perror("Could not make temporary directory");
210 return false;
211 }
212 #endif
213
214 path_ = std::move(path);
215 return true;
216 }
217
AddFile(const std::string & filename,bssl::Span<const uint8_t> content)218 bool TemporaryDirectory::AddFile(const std::string &filename,
219 bssl::Span<const uint8_t> content) {
220 if (path_.empty()) {
221 return false;
222 }
223
224 ScopedFILE file(fopen(GetFilePath(filename).c_str(), "wb"));
225 if (file == nullptr) {
226 perror("Could not open temporary file");
227 return false;
228 }
229 if (!content.empty() &&
230 fwrite(content.data(), content.size(), /*nitems=*/1, file.get()) != 1) {
231 perror("Could not write temporary file");
232 return false;
233 }
234
235 files_.insert(filename);
236 return true;
237 }
238
239 BSSL_NAMESPACE_END
240