xref: /llvm-project/flang/unittests/Runtime/AccessTest.cpp (revision 32403f79f4fcdb74b1576eed19cde7b104191808)
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 bool userSkipsPermissionChecks() {
36   // The tests in this file assume normal permission checks apply to the user
37   // running the tests. This isn't true when the test is run by root.
38   return geteuid() == 0;
39 }
40 
41 static std::string addPIDSuffix(const char *name) {
42   std::stringstream ss;
43   ss << name;
44   ss << '.';
45 
46   ss << getpid();
47 
48   return ss.str();
49 }
50 
51 static bool exists(const std::string &path) {
52   return access(path.c_str(), F_OK) == 0;
53 }
54 
55 // Implementation of std::filesystem::temp_directory_path adapted from libcxx
56 // See llvm-project/libcxx/src/filesystem/operations.cpp
57 // Using std::filesystem is inconvenient because the required flags are not
58 // consistent accross compilers and CMake doesn't have built in support to
59 // determine the correct flags.
60 static const char *temp_directory_path() {
61   // TODO: Windows
62   const char *env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
63   const char *ret = nullptr;
64 
65   for (auto &ep : env_paths) {
66     if ((ret = getenv(ep))) {
67       break;
68     }
69   }
70 
71   if (ret == nullptr) {
72 #if defined(__ANDROID__)
73     ret = "/data/local/tmp";
74 #else
75     ret = "/tmp";
76 #endif
77   }
78 
79   assert(exists(ret));
80   return ret;
81 }
82 
83 static std::string createTemporaryFile(
84     const char *name, const AccessType &accessType) {
85   std::string path =
86       (llvm::Twine{temp_directory_path()} + "/" + addPIDSuffix(name)).str();
87 
88   // O_CREAT | O_EXCL enforces that this file is newly created by this call.
89   // This feels risky. If we don't have permission to create files in the
90   // temporary directory or if the files already exist, the test will fail.
91   // But we can't use std::tmpfile() because we need a path to the file and
92   // to control the filesystem permissions
93   mode_t mode{0};
94   if (accessType.read) {
95     mode |= S_IRUSR;
96   }
97   if (accessType.write) {
98     mode |= S_IWUSR;
99   }
100   if (accessType.execute) {
101     mode |= S_IXUSR;
102   }
103 
104   int file = open(path.c_str(), O_CREAT | O_EXCL, mode);
105   if (file == -1) {
106     return {};
107   }
108 
109   close(file);
110 
111   return path;
112 }
113 
114 static std::int64_t callAccess(
115     const std::string &path, const AccessType &accessType) {
116   const char *cpath{path.c_str()};
117   std::int64_t pathlen = std::strlen(cpath);
118 
119   std::string mode;
120   if (accessType.read) {
121     mode += 'r';
122   }
123   if (accessType.write) {
124     mode += 'w';
125   }
126   if (accessType.execute) {
127     mode += 'x';
128   }
129   if (accessType.exists) {
130     mode += ' ';
131   }
132 
133   const char *cmode = mode.c_str();
134   std::int64_t modelen = std::strlen(cmode);
135 
136   return FORTRAN_PROCEDURE_NAME(access)(cpath, pathlen, cmode, modelen);
137 }
138 
139 TEST(AccessTests, TestExists) {
140   AccessType accessType;
141   accessType.exists = true;
142 
143   std::string path = createTemporaryFile(__func__, accessType);
144   ASSERT_FALSE(path.empty());
145 
146   std::int64_t res = callAccess(path, accessType);
147 
148   ASSERT_EQ(unlink(path.c_str()), 0);
149 
150   ASSERT_EQ(res, 0);
151 }
152 
153 TEST(AccessTests, TestNotExists) {
154   std::string nonExistant{addPIDSuffix(__func__)};
155   ASSERT_FALSE(exists(nonExistant));
156 
157   AccessType accessType;
158   accessType.exists = true;
159   std::int64_t res = callAccess(nonExistant, accessType);
160 
161   ASSERT_NE(res, 0);
162 }
163 
164 TEST(AccessTests, TestRead) {
165   AccessType accessType;
166   accessType.read = true;
167 
168   std::string path = createTemporaryFile(__func__, accessType);
169   ASSERT_FALSE(path.empty());
170 
171   std::int64_t res = callAccess(path, accessType);
172 
173   ASSERT_EQ(unlink(path.c_str()), 0);
174 
175   if (userSkipsPermissionChecks()) {
176     return;
177   }
178 
179   ASSERT_EQ(res, 0);
180 }
181 
182 TEST(AccessTests, TestNotRead) {
183   AccessType accessType;
184   accessType.read = false;
185 
186   std::string path = createTemporaryFile(__func__, accessType);
187   ASSERT_FALSE(path.empty());
188 
189   accessType.read = true;
190   std::int64_t res = callAccess(path, accessType);
191 
192   ASSERT_EQ(unlink(path.c_str()), 0);
193 
194   if (userSkipsPermissionChecks()) {
195     return;
196   }
197 
198   ASSERT_NE(res, 0);
199 }
200 
201 TEST(AccessTests, TestWrite) {
202   AccessType accessType;
203   accessType.write = true;
204 
205   std::string path = createTemporaryFile(__func__, accessType);
206   ASSERT_FALSE(path.empty());
207 
208   std::int64_t res = callAccess(path, accessType);
209 
210   ASSERT_EQ(unlink(path.c_str()), 0);
211 
212   if (userSkipsPermissionChecks()) {
213     return;
214   }
215 
216   ASSERT_EQ(res, 0);
217 }
218 
219 TEST(AccessTests, TestNotWrite) {
220   AccessType accessType;
221   accessType.write = false;
222 
223   std::string path = createTemporaryFile(__func__, accessType);
224   ASSERT_FALSE(path.empty());
225 
226   accessType.write = true;
227   std::int64_t res = callAccess(path, accessType);
228 
229   ASSERT_EQ(unlink(path.c_str()), 0);
230 
231   if (userSkipsPermissionChecks()) {
232     return;
233   }
234 
235   ASSERT_NE(res, 0);
236 }
237 
238 TEST(AccessTests, TestReadWrite) {
239   AccessType accessType;
240   accessType.read = true;
241   accessType.write = true;
242 
243   std::string path = createTemporaryFile(__func__, accessType);
244   ASSERT_FALSE(path.empty());
245 
246   std::int64_t res = callAccess(path, accessType);
247 
248   ASSERT_EQ(unlink(path.c_str()), 0);
249 
250   if (userSkipsPermissionChecks()) {
251     return;
252   }
253 
254   ASSERT_EQ(res, 0);
255 }
256 
257 TEST(AccessTests, TestNotReadWrite0) {
258   AccessType accessType;
259   accessType.read = false;
260   accessType.write = false;
261 
262   std::string path = createTemporaryFile(__func__, accessType);
263   ASSERT_FALSE(path.empty());
264 
265   accessType.read = true;
266   accessType.write = true;
267   std::int64_t res = callAccess(path, accessType);
268 
269   ASSERT_EQ(unlink(path.c_str()), 0);
270 
271   if (userSkipsPermissionChecks()) {
272     return;
273   }
274 
275   ASSERT_NE(res, 0);
276 }
277 
278 TEST(AccessTests, TestNotReadWrite1) {
279   AccessType accessType;
280   accessType.read = true;
281   accessType.write = false;
282 
283   std::string path = createTemporaryFile(__func__, accessType);
284   ASSERT_FALSE(path.empty());
285 
286   accessType.read = true;
287   accessType.write = true;
288   std::int64_t res = callAccess(path, accessType);
289 
290   ASSERT_EQ(unlink(path.c_str()), 0);
291 
292   if (userSkipsPermissionChecks()) {
293     return;
294   }
295 
296   ASSERT_NE(res, 0);
297 }
298 
299 TEST(AccessTests, TestNotReadWrite2) {
300   AccessType accessType;
301   accessType.read = false;
302   accessType.write = true;
303 
304   std::string path = createTemporaryFile(__func__, accessType);
305   ASSERT_FALSE(path.empty());
306 
307   accessType.read = true;
308   accessType.write = true;
309   std::int64_t res = callAccess(path, accessType);
310 
311   ASSERT_EQ(unlink(path.c_str()), 0);
312 
313   if (userSkipsPermissionChecks()) {
314     return;
315   }
316 
317   ASSERT_NE(res, 0);
318 }
319 
320 TEST(AccessTests, TestExecute) {
321   AccessType accessType;
322   accessType.execute = true;
323 
324   std::string path = createTemporaryFile(__func__, accessType);
325   ASSERT_FALSE(path.empty());
326 
327   std::int64_t res = callAccess(path, accessType);
328 
329   ASSERT_EQ(unlink(path.c_str()), 0);
330 
331   if (userSkipsPermissionChecks()) {
332     return;
333   }
334 
335   ASSERT_EQ(res, 0);
336 }
337 
338 TEST(AccessTests, TestNotExecute) {
339   AccessType accessType;
340   accessType.execute = false;
341 
342   std::string path = createTemporaryFile(__func__, accessType);
343   ASSERT_FALSE(path.empty());
344 
345   accessType.execute = true;
346   std::int64_t res = callAccess(path, accessType);
347 
348   ASSERT_EQ(unlink(path.c_str()), 0);
349 
350   if (userSkipsPermissionChecks()) {
351     return;
352   }
353 
354   ASSERT_NE(res, 0);
355 }
356 
357 TEST(AccessTests, TestRWX) {
358   AccessType accessType;
359   accessType.read = true;
360   accessType.write = true;
361   accessType.execute = true;
362 
363   std::string path = createTemporaryFile(__func__, accessType);
364   ASSERT_FALSE(path.empty());
365 
366   std::int64_t res = callAccess(path, accessType);
367 
368   ASSERT_EQ(unlink(path.c_str()), 0);
369 
370   if (userSkipsPermissionChecks()) {
371     return;
372   }
373 
374   ASSERT_EQ(res, 0);
375 }
376 
377 TEST(AccessTests, TestNotRWX0) {
378   AccessType accessType;
379   accessType.read = false;
380   accessType.write = false;
381   accessType.execute = false;
382 
383   std::string path = createTemporaryFile(__func__, accessType);
384   ASSERT_FALSE(path.empty());
385 
386   accessType.read = true;
387   accessType.write = true;
388   accessType.execute = true;
389   std::int64_t res = callAccess(path, accessType);
390 
391   ASSERT_EQ(unlink(path.c_str()), 0);
392 
393   if (userSkipsPermissionChecks()) {
394     return;
395   }
396 
397   ASSERT_NE(res, 0);
398 }
399 
400 TEST(AccessTests, TestNotRWX1) {
401   AccessType accessType;
402   accessType.read = true;
403   accessType.write = false;
404   accessType.execute = false;
405 
406   std::string path = createTemporaryFile(__func__, accessType);
407   ASSERT_FALSE(path.empty());
408 
409   accessType.read = true;
410   accessType.write = true;
411   accessType.execute = true;
412   std::int64_t res = callAccess(path, accessType);
413 
414   ASSERT_EQ(unlink(path.c_str()), 0);
415 
416   if (userSkipsPermissionChecks()) {
417     return;
418   }
419 
420   ASSERT_NE(res, 0);
421 }
422 
423 TEST(AccessTests, TestNotRWX2) {
424   AccessType accessType;
425   accessType.read = true;
426   accessType.write = true;
427   accessType.execute = false;
428 
429   std::string path = createTemporaryFile(__func__, accessType);
430   ASSERT_FALSE(path.empty());
431 
432   accessType.read = true;
433   accessType.write = true;
434   accessType.execute = true;
435   std::int64_t res = callAccess(path, accessType);
436 
437   ASSERT_EQ(unlink(path.c_str()), 0);
438 
439   if (userSkipsPermissionChecks()) {
440     return;
441   }
442 
443   ASSERT_NE(res, 0);
444 }
445 
446 TEST(AccessTests, TestNotRWX3) {
447   AccessType accessType;
448   accessType.read = true;
449   accessType.write = false;
450   accessType.execute = true;
451 
452   std::string path = createTemporaryFile(__func__, accessType);
453   ASSERT_FALSE(path.empty());
454 
455   accessType.read = true;
456   accessType.write = true;
457   accessType.execute = true;
458   std::int64_t res = callAccess(path, accessType);
459 
460   ASSERT_EQ(unlink(path.c_str()), 0);
461 
462   if (userSkipsPermissionChecks()) {
463     return;
464   }
465 
466   ASSERT_NE(res, 0);
467 }
468 
469 TEST(AccessTests, TestNotRWX4) {
470   AccessType accessType;
471   accessType.read = false;
472   accessType.write = true;
473   accessType.execute = true;
474 
475   std::string path = createTemporaryFile(__func__, accessType);
476   ASSERT_FALSE(path.empty());
477 
478   accessType.read = true;
479   accessType.write = true;
480   accessType.execute = true;
481   std::int64_t res = callAccess(path, accessType);
482 
483   ASSERT_EQ(unlink(path.c_str()), 0);
484 
485   if (userSkipsPermissionChecks()) {
486     return;
487   }
488 
489   ASSERT_NE(res, 0);
490 }
491 
492 #endif // !_WIN32
493