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