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