| //===-- flang/unittests/Runtime/AccessTest.cpp ----------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // TODO: ACCESS is not yet implemented on Windows |
| #ifndef _WIN32 |
| |
| #include "CrashHandlerFixture.h" |
| #include "gtest/gtest.h" |
| #include "flang/Runtime/extensions.h" |
| #include "llvm/ADT/Twine.h" |
| |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| namespace { |
| |
| struct AccessTests : public CrashHandlerFixture {}; |
| |
| struct AccessType { |
| bool read{false}; |
| bool write{false}; |
| bool execute{false}; |
| bool exists{false}; |
| }; |
| |
| } // namespace |
| |
| static std::string addPIDSuffix(const char *name) { |
| std::stringstream ss; |
| ss << name; |
| ss << '.'; |
| |
| ss << getpid(); |
| |
| return ss.str(); |
| } |
| |
| static bool exists(const std::string &path) { |
| return access(path.c_str(), F_OK) == 0; |
| } |
| |
| // Implementation of std::filesystem::temp_directory_path adapted from libcxx |
| // See llvm-project/libcxx/src/filesystem/operations.cpp |
| // Using std::filesystem is inconvenient because the required flags are not |
| // consistent accross compilers and CMake doesn't have built in support to |
| // determine the correct flags. |
| static const char *temp_directory_path() { |
| // TODO: Windows |
| const char *env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}; |
| const char *ret = nullptr; |
| |
| for (auto &ep : env_paths) { |
| if ((ret = getenv(ep))) { |
| break; |
| } |
| } |
| |
| if (ret == nullptr) { |
| #if defined(__ANDROID__) |
| ret = "/data/local/tmp"; |
| #else |
| ret = "/tmp"; |
| #endif |
| } |
| |
| assert(exists(ret)); |
| return ret; |
| } |
| |
| static std::string createTemporaryFile( |
| const char *name, const AccessType &accessType) { |
| std::string path = |
| (llvm::Twine{temp_directory_path()} + "/" + addPIDSuffix(name)).str(); |
| |
| // O_CREAT | O_EXCL enforces that this file is newly created by this call. |
| // This feels risky. If we don't have permission to create files in the |
| // temporary directory or if the files already exist, the test will fail. |
| // But we can't use std::tmpfile() because we need a path to the file and |
| // to control the filesystem permissions |
| mode_t mode{0}; |
| if (accessType.read) { |
| mode |= S_IRUSR; |
| } |
| if (accessType.write) { |
| mode |= S_IWUSR; |
| } |
| if (accessType.execute) { |
| mode |= S_IXUSR; |
| } |
| |
| int file = open(path.c_str(), O_CREAT | O_EXCL, mode); |
| if (file == -1) { |
| return {}; |
| } |
| |
| close(file); |
| |
| return path; |
| } |
| |
| static std::int64_t callAccess( |
| const std::string &path, const AccessType &accessType) { |
| const char *cpath{path.c_str()}; |
| std::int64_t pathlen = std::strlen(cpath); |
| |
| std::string mode; |
| if (accessType.read) { |
| mode += 'r'; |
| } |
| if (accessType.write) { |
| mode += 'w'; |
| } |
| if (accessType.execute) { |
| mode += 'x'; |
| } |
| if (accessType.exists) { |
| mode += ' '; |
| } |
| |
| const char *cmode = mode.c_str(); |
| std::int64_t modelen = std::strlen(cmode); |
| |
| return FORTRAN_PROCEDURE_NAME(access)(cpath, pathlen, cmode, modelen); |
| } |
| |
| TEST(AccessTests, TestExists) { |
| AccessType accessType; |
| accessType.exists = true; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_EQ(res, 0); |
| } |
| |
| TEST(AccessTests, TestNotExists) { |
| std::string nonExistant{addPIDSuffix(__func__)}; |
| ASSERT_FALSE(exists(nonExistant)); |
| |
| AccessType accessType; |
| accessType.exists = true; |
| std::int64_t res = callAccess(nonExistant, accessType); |
| |
| ASSERT_NE(res, 0); |
| } |
| |
| TEST(AccessTests, TestRead) { |
| AccessType accessType; |
| accessType.read = true; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_EQ(res, 0); |
| } |
| |
| TEST(AccessTests, TestNotRead) { |
| AccessType accessType; |
| accessType.read = false; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| accessType.read = true; |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_NE(res, 0); |
| } |
| |
| TEST(AccessTests, TestWrite) { |
| AccessType accessType; |
| accessType.write = true; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_EQ(res, 0); |
| } |
| |
| TEST(AccessTests, TestNotWrite) { |
| AccessType accessType; |
| accessType.write = false; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| accessType.write = true; |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_NE(res, 0); |
| } |
| |
| TEST(AccessTests, TestReadWrite) { |
| AccessType accessType; |
| accessType.read = true; |
| accessType.write = true; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_EQ(res, 0); |
| } |
| |
| TEST(AccessTests, TestNotReadWrite0) { |
| AccessType accessType; |
| accessType.read = false; |
| accessType.write = false; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| accessType.read = true; |
| accessType.write = true; |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_NE(res, 0); |
| } |
| |
| TEST(AccessTests, TestNotReadWrite1) { |
| AccessType accessType; |
| accessType.read = true; |
| accessType.write = false; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| accessType.read = true; |
| accessType.write = true; |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_NE(res, 0); |
| } |
| |
| TEST(AccessTests, TestNotReadWrite2) { |
| AccessType accessType; |
| accessType.read = false; |
| accessType.write = true; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| accessType.read = true; |
| accessType.write = true; |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_NE(res, 0); |
| } |
| |
| TEST(AccessTests, TestExecute) { |
| AccessType accessType; |
| accessType.execute = true; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_EQ(res, 0); |
| } |
| |
| TEST(AccessTests, TestNotExecute) { |
| AccessType accessType; |
| accessType.execute = false; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| accessType.execute = true; |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_NE(res, 0); |
| } |
| |
| TEST(AccessTests, TestRWX) { |
| AccessType accessType; |
| accessType.read = true; |
| accessType.write = true; |
| accessType.execute = true; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_EQ(res, 0); |
| } |
| |
| TEST(AccessTests, TestNotRWX0) { |
| AccessType accessType; |
| accessType.read = false; |
| accessType.write = false; |
| accessType.execute = false; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| accessType.read = true; |
| accessType.write = true; |
| accessType.execute = true; |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_NE(res, 0); |
| } |
| |
| TEST(AccessTests, TestNotRWX1) { |
| AccessType accessType; |
| accessType.read = true; |
| accessType.write = false; |
| accessType.execute = false; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| accessType.read = true; |
| accessType.write = true; |
| accessType.execute = true; |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_NE(res, 0); |
| } |
| |
| TEST(AccessTests, TestNotRWX2) { |
| AccessType accessType; |
| accessType.read = true; |
| accessType.write = true; |
| accessType.execute = false; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| accessType.read = true; |
| accessType.write = true; |
| accessType.execute = true; |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_NE(res, 0); |
| } |
| |
| TEST(AccessTests, TestNotRWX3) { |
| AccessType accessType; |
| accessType.read = true; |
| accessType.write = false; |
| accessType.execute = true; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| accessType.read = true; |
| accessType.write = true; |
| accessType.execute = true; |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_NE(res, 0); |
| } |
| |
| TEST(AccessTests, TestNotRWX4) { |
| AccessType accessType; |
| accessType.read = false; |
| accessType.write = true; |
| accessType.execute = true; |
| |
| std::string path = createTemporaryFile(__func__, accessType); |
| ASSERT_FALSE(path.empty()); |
| |
| accessType.read = true; |
| accessType.write = true; |
| accessType.execute = true; |
| std::int64_t res = callAccess(path, accessType); |
| |
| ASSERT_EQ(unlink(path.c_str()), 0); |
| |
| ASSERT_NE(res, 0); |
| } |
| |
| #endif // !_WIN32 |