
Mostly mechanical changes in preparation of extracting the Flang-RT "subproject" in #110217. This PR intends to only move pre-existing files to the new folder structure, with no behavioral change. Common files (headers, testing, cmake) shared by Flang-RT and Flang remain in `flang/`. Some cosmetic changes and files paths were necessary: * Relative paths to the new path for the source files and `add_subdirectory`. * Add the new location's include directory to `include_directories` * The unittest/Evaluate directory has unitests for flang-rt and Flang. A new `CMakeLists.txt` was introduced for the flang-rt tests. * Change the `#include` paths relative to the include directive * clang-format on the `#include` directives * Since the paths are part if the copyright header and include guards, a script was used to canonicalize those * `test/Runtime` and runtime tests in `test/Driver` are moved, but the lit.cfg.py mechanism to execute the will only be added in #110217.
493 lines
10 KiB
C++
493 lines
10 KiB
C++
//===-- unittests/Runtime/AccessTest.cpp ------------------------*- C++ -*-===//
|
|
//
|
|
// 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 bool userSkipsPermissionChecks() {
|
|
// The tests in this file assume normal permission checks apply to the user
|
|
// running the tests. This isn't true when the test is run by root.
|
|
return geteuid() == 0;
|
|
}
|
|
|
|
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);
|
|
|
|
if (userSkipsPermissionChecks()) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
if (userSkipsPermissionChecks()) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
if (userSkipsPermissionChecks()) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
if (userSkipsPermissionChecks()) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
if (userSkipsPermissionChecks()) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
if (userSkipsPermissionChecks()) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
if (userSkipsPermissionChecks()) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
if (userSkipsPermissionChecks()) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
if (userSkipsPermissionChecks()) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
if (userSkipsPermissionChecks()) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
if (userSkipsPermissionChecks()) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
if (userSkipsPermissionChecks()) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
if (userSkipsPermissionChecks()) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
if (userSkipsPermissionChecks()) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
if (userSkipsPermissionChecks()) {
|
|
return;
|
|
}
|
|
|
|
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);
|
|
|
|
if (userSkipsPermissionChecks()) {
|
|
return;
|
|
}
|
|
|
|
ASSERT_NE(res, 0);
|
|
}
|
|
|
|
#endif // !_WIN32
|