1 | //===-- flang/unittests/Runtime/AccessTest.cpp ----------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | // TODO: ACCESS is not yet implemented on Windows |
10 | #ifndef _WIN32 |
11 | |
12 | #include "CrashHandlerFixture.h" |
13 | #include "gtest/gtest.h" |
14 | #include "flang/Runtime/extensions.h" |
15 | #include "llvm/ADT/Twine.h" |
16 | |
17 | #include <fcntl.h> |
18 | #include <sys/stat.h> |
19 | #include <sys/types.h> |
20 | #include <unistd.h> |
21 | |
22 | namespace { |
23 | |
24 | struct AccessTests : public CrashHandlerFixture {}; |
25 | |
26 | struct AccessType { |
27 | bool read{false}; |
28 | bool write{false}; |
29 | bool execute{false}; |
30 | bool exists{false}; |
31 | }; |
32 | |
33 | } // namespace |
34 | |
35 | static std::string addPIDSuffix(const char *name) { |
36 | std::stringstream ss; |
37 | ss << name; |
38 | ss << '.'; |
39 | |
40 | ss << getpid(); |
41 | |
42 | return ss.str(); |
43 | } |
44 | |
45 | static bool exists(const std::string &path) { |
46 | return access(name: path.c_str(), F_OK) == 0; |
47 | } |
48 | |
49 | // Implementation of std::filesystem::temp_directory_path adapted from libcxx |
50 | // See llvm-project/libcxx/src/filesystem/operations.cpp |
51 | // Using std::filesystem is inconvenient because the required flags are not |
52 | // consistent accross compilers and CMake doesn't have built in support to |
53 | // determine the correct flags. |
54 | static const char *temp_directory_path() { |
55 | // TODO: Windows |
56 | const char *env_paths[] = {"TMPDIR" , "TMP" , "TEMP" , "TEMPDIR" }; |
57 | const char *ret = nullptr; |
58 | |
59 | for (auto &ep : env_paths) { |
60 | if ((ret = getenv(name: ep))) { |
61 | break; |
62 | } |
63 | } |
64 | |
65 | if (ret == nullptr) { |
66 | #if defined(__ANDROID__) |
67 | ret = "/data/local/tmp" ; |
68 | #else |
69 | ret = "/tmp" ; |
70 | #endif |
71 | } |
72 | |
73 | assert(exists(ret)); |
74 | return ret; |
75 | } |
76 | |
77 | static std::string createTemporaryFile( |
78 | const char *name, const AccessType &accessType) { |
79 | std::string path = |
80 | (llvm::Twine{temp_directory_path()} + "/" + addPIDSuffix(name)).str(); |
81 | |
82 | // O_CREAT | O_EXCL enforces that this file is newly created by this call. |
83 | // This feels risky. If we don't have permission to create files in the |
84 | // temporary directory or if the files already exist, the test will fail. |
85 | // But we can't use std::tmpfile() because we need a path to the file and |
86 | // to control the filesystem permissions |
87 | mode_t mode{0}; |
88 | if (accessType.read) { |
89 | mode |= S_IRUSR; |
90 | } |
91 | if (accessType.write) { |
92 | mode |= S_IWUSR; |
93 | } |
94 | if (accessType.execute) { |
95 | mode |= S_IXUSR; |
96 | } |
97 | |
98 | int file = open(file: path.c_str(), O_CREAT | O_EXCL, mode); |
99 | if (file == -1) { |
100 | return {}; |
101 | } |
102 | |
103 | close(fd: file); |
104 | |
105 | return path; |
106 | } |
107 | |
108 | static std::int64_t callAccess( |
109 | const std::string &path, const AccessType &accessType) { |
110 | const char *cpath{path.c_str()}; |
111 | std::int64_t pathlen = std::strlen(s: cpath); |
112 | |
113 | std::string mode; |
114 | if (accessType.read) { |
115 | mode += 'r'; |
116 | } |
117 | if (accessType.write) { |
118 | mode += 'w'; |
119 | } |
120 | if (accessType.execute) { |
121 | mode += 'x'; |
122 | } |
123 | if (accessType.exists) { |
124 | mode += ' '; |
125 | } |
126 | |
127 | const char *cmode = mode.c_str(); |
128 | std::int64_t modelen = std::strlen(s: cmode); |
129 | |
130 | return FORTRAN_PROCEDURE_NAME(access)(cpath, pathlen, cmode, modelen); |
131 | } |
132 | |
133 | TEST(AccessTests, TestExists) { |
134 | AccessType accessType; |
135 | accessType.exists = true; |
136 | |
137 | std::string path = createTemporaryFile(name: __func__, accessType); |
138 | ASSERT_FALSE(path.empty()); |
139 | |
140 | std::int64_t res = callAccess(path, accessType); |
141 | |
142 | ASSERT_EQ(unlink(path.c_str()), 0); |
143 | |
144 | ASSERT_EQ(res, 0); |
145 | } |
146 | |
147 | TEST(AccessTests, TestNotExists) { |
148 | std::string nonExistant{addPIDSuffix(name: __func__)}; |
149 | ASSERT_FALSE(exists(nonExistant)); |
150 | |
151 | AccessType accessType; |
152 | accessType.exists = true; |
153 | std::int64_t res = callAccess(path: nonExistant, accessType); |
154 | |
155 | ASSERT_NE(res, 0); |
156 | } |
157 | |
158 | TEST(AccessTests, TestRead) { |
159 | AccessType accessType; |
160 | accessType.read = true; |
161 | |
162 | std::string path = createTemporaryFile(name: __func__, accessType); |
163 | ASSERT_FALSE(path.empty()); |
164 | |
165 | std::int64_t res = callAccess(path, accessType); |
166 | |
167 | ASSERT_EQ(unlink(path.c_str()), 0); |
168 | |
169 | ASSERT_EQ(res, 0); |
170 | } |
171 | |
172 | TEST(AccessTests, TestNotRead) { |
173 | AccessType accessType; |
174 | accessType.read = false; |
175 | |
176 | std::string path = createTemporaryFile(name: __func__, accessType); |
177 | ASSERT_FALSE(path.empty()); |
178 | |
179 | accessType.read = true; |
180 | std::int64_t res = callAccess(path, accessType); |
181 | |
182 | ASSERT_EQ(unlink(path.c_str()), 0); |
183 | |
184 | ASSERT_NE(res, 0); |
185 | } |
186 | |
187 | TEST(AccessTests, TestWrite) { |
188 | AccessType accessType; |
189 | accessType.write = true; |
190 | |
191 | std::string path = createTemporaryFile(name: __func__, accessType); |
192 | ASSERT_FALSE(path.empty()); |
193 | |
194 | std::int64_t res = callAccess(path, accessType); |
195 | |
196 | ASSERT_EQ(unlink(path.c_str()), 0); |
197 | |
198 | ASSERT_EQ(res, 0); |
199 | } |
200 | |
201 | TEST(AccessTests, TestNotWrite) { |
202 | AccessType accessType; |
203 | accessType.write = false; |
204 | |
205 | std::string path = createTemporaryFile(name: __func__, accessType); |
206 | ASSERT_FALSE(path.empty()); |
207 | |
208 | accessType.write = true; |
209 | std::int64_t res = callAccess(path, accessType); |
210 | |
211 | ASSERT_EQ(unlink(path.c_str()), 0); |
212 | |
213 | ASSERT_NE(res, 0); |
214 | } |
215 | |
216 | TEST(AccessTests, TestReadWrite) { |
217 | AccessType accessType; |
218 | accessType.read = true; |
219 | accessType.write = true; |
220 | |
221 | std::string path = createTemporaryFile(name: __func__, accessType); |
222 | ASSERT_FALSE(path.empty()); |
223 | |
224 | std::int64_t res = callAccess(path, accessType); |
225 | |
226 | ASSERT_EQ(unlink(path.c_str()), 0); |
227 | |
228 | ASSERT_EQ(res, 0); |
229 | } |
230 | |
231 | TEST(AccessTests, TestNotReadWrite0) { |
232 | AccessType accessType; |
233 | accessType.read = false; |
234 | accessType.write = false; |
235 | |
236 | std::string path = createTemporaryFile(name: __func__, accessType); |
237 | ASSERT_FALSE(path.empty()); |
238 | |
239 | accessType.read = true; |
240 | accessType.write = true; |
241 | std::int64_t res = callAccess(path, accessType); |
242 | |
243 | ASSERT_EQ(unlink(path.c_str()), 0); |
244 | |
245 | ASSERT_NE(res, 0); |
246 | } |
247 | |
248 | TEST(AccessTests, TestNotReadWrite1) { |
249 | AccessType accessType; |
250 | accessType.read = true; |
251 | accessType.write = false; |
252 | |
253 | std::string path = createTemporaryFile(name: __func__, accessType); |
254 | ASSERT_FALSE(path.empty()); |
255 | |
256 | accessType.read = true; |
257 | accessType.write = true; |
258 | std::int64_t res = callAccess(path, accessType); |
259 | |
260 | ASSERT_EQ(unlink(path.c_str()), 0); |
261 | |
262 | ASSERT_NE(res, 0); |
263 | } |
264 | |
265 | TEST(AccessTests, TestNotReadWrite2) { |
266 | AccessType accessType; |
267 | accessType.read = false; |
268 | accessType.write = true; |
269 | |
270 | std::string path = createTemporaryFile(name: __func__, accessType); |
271 | ASSERT_FALSE(path.empty()); |
272 | |
273 | accessType.read = true; |
274 | accessType.write = true; |
275 | std::int64_t res = callAccess(path, accessType); |
276 | |
277 | ASSERT_EQ(unlink(path.c_str()), 0); |
278 | |
279 | ASSERT_NE(res, 0); |
280 | } |
281 | |
282 | TEST(AccessTests, TestExecute) { |
283 | AccessType accessType; |
284 | accessType.execute = true; |
285 | |
286 | std::string path = createTemporaryFile(name: __func__, accessType); |
287 | ASSERT_FALSE(path.empty()); |
288 | |
289 | std::int64_t res = callAccess(path, accessType); |
290 | |
291 | ASSERT_EQ(unlink(path.c_str()), 0); |
292 | |
293 | ASSERT_EQ(res, 0); |
294 | } |
295 | |
296 | TEST(AccessTests, TestNotExecute) { |
297 | AccessType accessType; |
298 | accessType.execute = false; |
299 | |
300 | std::string path = createTemporaryFile(name: __func__, accessType); |
301 | ASSERT_FALSE(path.empty()); |
302 | |
303 | accessType.execute = true; |
304 | std::int64_t res = callAccess(path, accessType); |
305 | |
306 | ASSERT_EQ(unlink(path.c_str()), 0); |
307 | |
308 | ASSERT_NE(res, 0); |
309 | } |
310 | |
311 | TEST(AccessTests, TestRWX) { |
312 | AccessType accessType; |
313 | accessType.read = true; |
314 | accessType.write = true; |
315 | accessType.execute = true; |
316 | |
317 | std::string path = createTemporaryFile(name: __func__, accessType); |
318 | ASSERT_FALSE(path.empty()); |
319 | |
320 | std::int64_t res = callAccess(path, accessType); |
321 | |
322 | ASSERT_EQ(unlink(path.c_str()), 0); |
323 | |
324 | ASSERT_EQ(res, 0); |
325 | } |
326 | |
327 | TEST(AccessTests, TestNotRWX0) { |
328 | AccessType accessType; |
329 | accessType.read = false; |
330 | accessType.write = false; |
331 | accessType.execute = false; |
332 | |
333 | std::string path = createTemporaryFile(name: __func__, accessType); |
334 | ASSERT_FALSE(path.empty()); |
335 | |
336 | accessType.read = true; |
337 | accessType.write = true; |
338 | accessType.execute = true; |
339 | std::int64_t res = callAccess(path, accessType); |
340 | |
341 | ASSERT_EQ(unlink(path.c_str()), 0); |
342 | |
343 | ASSERT_NE(res, 0); |
344 | } |
345 | |
346 | TEST(AccessTests, TestNotRWX1) { |
347 | AccessType accessType; |
348 | accessType.read = true; |
349 | accessType.write = false; |
350 | accessType.execute = false; |
351 | |
352 | std::string path = createTemporaryFile(name: __func__, accessType); |
353 | ASSERT_FALSE(path.empty()); |
354 | |
355 | accessType.read = true; |
356 | accessType.write = true; |
357 | accessType.execute = true; |
358 | std::int64_t res = callAccess(path, accessType); |
359 | |
360 | ASSERT_EQ(unlink(path.c_str()), 0); |
361 | |
362 | ASSERT_NE(res, 0); |
363 | } |
364 | |
365 | TEST(AccessTests, TestNotRWX2) { |
366 | AccessType accessType; |
367 | accessType.read = true; |
368 | accessType.write = true; |
369 | accessType.execute = false; |
370 | |
371 | std::string path = createTemporaryFile(name: __func__, accessType); |
372 | ASSERT_FALSE(path.empty()); |
373 | |
374 | accessType.read = true; |
375 | accessType.write = true; |
376 | accessType.execute = true; |
377 | std::int64_t res = callAccess(path, accessType); |
378 | |
379 | ASSERT_EQ(unlink(path.c_str()), 0); |
380 | |
381 | ASSERT_NE(res, 0); |
382 | } |
383 | |
384 | TEST(AccessTests, TestNotRWX3) { |
385 | AccessType accessType; |
386 | accessType.read = true; |
387 | accessType.write = false; |
388 | accessType.execute = true; |
389 | |
390 | std::string path = createTemporaryFile(name: __func__, accessType); |
391 | ASSERT_FALSE(path.empty()); |
392 | |
393 | accessType.read = true; |
394 | accessType.write = true; |
395 | accessType.execute = true; |
396 | std::int64_t res = callAccess(path, accessType); |
397 | |
398 | ASSERT_EQ(unlink(path.c_str()), 0); |
399 | |
400 | ASSERT_NE(res, 0); |
401 | } |
402 | |
403 | TEST(AccessTests, TestNotRWX4) { |
404 | AccessType accessType; |
405 | accessType.read = false; |
406 | accessType.write = true; |
407 | accessType.execute = true; |
408 | |
409 | std::string path = createTemporaryFile(name: __func__, accessType); |
410 | ASSERT_FALSE(path.empty()); |
411 | |
412 | accessType.read = true; |
413 | accessType.write = true; |
414 | accessType.execute = true; |
415 | std::int64_t res = callAccess(path, accessType); |
416 | |
417 | ASSERT_EQ(unlink(path.c_str()), 0); |
418 | |
419 | ASSERT_NE(res, 0); |
420 | } |
421 | |
422 | #endif // !_WIN32 |
423 | |