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
22namespace {
23
24struct AccessTests : public CrashHandlerFixture {};
25
26struct AccessType {
27 bool read{false};
28 bool write{false};
29 bool execute{false};
30 bool exists{false};
31};
32
33} // namespace
34
35static 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
45static 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.
54static 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
77static 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
108static 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
133TEST(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
147TEST(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
158TEST(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
172TEST(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
187TEST(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
201TEST(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
216TEST(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
231TEST(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
248TEST(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
265TEST(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
282TEST(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
296TEST(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
311TEST(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
327TEST(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
346TEST(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
365TEST(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
384TEST(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
403TEST(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

source code of flang/unittests/Runtime/AccessTest.cpp