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 16 #include <fcntl.h> 17 #include <filesystem> 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 std::string addPIDSuffix(const char *name) { 36 std::stringstream ss; 37 ss << name; 38 ss << '.'; 39 40 ss << getpid(); 41 42 return ss.str(); 43 } 44 45 static std::filesystem::path createTemporaryFile( 46 const char *name, const AccessType &accessType) { 47 std::filesystem::path path{ 48 std::filesystem::temp_directory_path() / addPIDSuffix(name)}; 49 50 // O_CREAT | O_EXCL enforces that this file is newly created by this call. 51 // This feels risky. If we don't have permission to create files in the 52 // temporary directory or if the files already exist, the test will fail. 53 // But we can't use std::tmpfile() because we need a path to the file and 54 // to control the filesystem permissions 55 mode_t mode{0}; 56 if (accessType.read) { 57 mode |= S_IRUSR; 58 } 59 if (accessType.write) { 60 mode |= S_IWUSR; 61 } 62 if (accessType.execute) { 63 mode |= S_IXUSR; 64 } 65 66 int file = open(path.c_str(), O_CREAT | O_EXCL, mode); 67 if (file == -1) { 68 return {}; 69 } 70 71 close(file); 72 73 return path; 74 } 75 76 static std::int64_t callAccess( 77 const std::filesystem::path &path, const AccessType &accessType) { 78 const char *cpath{path.c_str()}; 79 std::int64_t pathlen = std::strlen(cpath); 80 81 std::string mode; 82 if (accessType.read) { 83 mode += 'r'; 84 } 85 if (accessType.write) { 86 mode += 'w'; 87 } 88 if (accessType.execute) { 89 mode += 'x'; 90 } 91 if (accessType.exists) { 92 mode += ' '; 93 } 94 95 const char *cmode = mode.c_str(); 96 std::int64_t modelen = std::strlen(cmode); 97 98 return FORTRAN_PROCEDURE_NAME(access)(cpath, pathlen, cmode, modelen); 99 } 100 101 TEST(AccessTests, TestExists) { 102 AccessType accessType; 103 accessType.exists = true; 104 105 std::filesystem::path path = createTemporaryFile(__func__, accessType); 106 ASSERT_FALSE(path.empty()); 107 108 std::int64_t res = callAccess(path, accessType); 109 110 std::filesystem::remove(path); 111 112 ASSERT_EQ(res, 0); 113 } 114 115 TEST(AccessTests, TestNotExists) { 116 std::filesystem::path nonExistant{addPIDSuffix(__func__)}; 117 ASSERT_FALSE(std::filesystem::exists(nonExistant)); 118 119 AccessType accessType; 120 accessType.exists = true; 121 std::int64_t res = callAccess(nonExistant, accessType); 122 123 ASSERT_NE(res, 0); 124 } 125 126 TEST(AccessTests, TestRead) { 127 AccessType accessType; 128 accessType.read = true; 129 130 std::filesystem::path path = createTemporaryFile(__func__, accessType); 131 ASSERT_FALSE(path.empty()); 132 133 std::int64_t res = callAccess(path, accessType); 134 135 std::filesystem::remove(path); 136 137 ASSERT_EQ(res, 0); 138 } 139 140 TEST(AccessTests, TestNotRead) { 141 AccessType accessType; 142 accessType.read = false; 143 144 std::filesystem::path path = createTemporaryFile(__func__, accessType); 145 ASSERT_FALSE(path.empty()); 146 147 accessType.read = true; 148 std::int64_t res = callAccess(path, accessType); 149 150 std::filesystem::remove(path); 151 152 ASSERT_NE(res, 0); 153 } 154 155 TEST(AccessTests, TestWrite) { 156 AccessType accessType; 157 accessType.write = true; 158 159 std::filesystem::path path = createTemporaryFile(__func__, accessType); 160 ASSERT_FALSE(path.empty()); 161 162 std::int64_t res = callAccess(path, accessType); 163 164 std::filesystem::remove(path); 165 166 ASSERT_EQ(res, 0); 167 } 168 169 TEST(AccessTests, TestNotWrite) { 170 AccessType accessType; 171 accessType.write = false; 172 173 std::filesystem::path path = createTemporaryFile(__func__, accessType); 174 ASSERT_FALSE(path.empty()); 175 176 accessType.write = true; 177 std::int64_t res = callAccess(path, accessType); 178 179 std::filesystem::remove(path); 180 181 ASSERT_NE(res, 0); 182 } 183 184 TEST(AccessTests, TestReadWrite) { 185 AccessType accessType; 186 accessType.read = true; 187 accessType.write = true; 188 189 std::filesystem::path path = createTemporaryFile(__func__, accessType); 190 ASSERT_FALSE(path.empty()); 191 192 std::int64_t res = callAccess(path, accessType); 193 194 std::filesystem::remove(path); 195 196 ASSERT_EQ(res, 0); 197 } 198 199 TEST(AccessTests, TestNotReadWrite0) { 200 AccessType accessType; 201 accessType.read = false; 202 accessType.write = false; 203 204 std::filesystem::path path = createTemporaryFile(__func__, accessType); 205 ASSERT_FALSE(path.empty()); 206 207 accessType.read = true; 208 accessType.write = true; 209 std::int64_t res = callAccess(path, accessType); 210 211 std::filesystem::remove(path); 212 213 ASSERT_NE(res, 0); 214 } 215 216 TEST(AccessTests, TestNotReadWrite1) { 217 AccessType accessType; 218 accessType.read = true; 219 accessType.write = false; 220 221 std::filesystem::path path = createTemporaryFile(__func__, accessType); 222 ASSERT_FALSE(path.empty()); 223 224 accessType.read = true; 225 accessType.write = true; 226 std::int64_t res = callAccess(path, accessType); 227 228 std::filesystem::remove(path); 229 230 ASSERT_NE(res, 0); 231 } 232 233 TEST(AccessTests, TestNotReadWrite2) { 234 AccessType accessType; 235 accessType.read = false; 236 accessType.write = true; 237 238 std::filesystem::path path = createTemporaryFile(__func__, accessType); 239 ASSERT_FALSE(path.empty()); 240 241 accessType.read = true; 242 accessType.write = true; 243 std::int64_t res = callAccess(path, accessType); 244 245 std::filesystem::remove(path); 246 247 ASSERT_NE(res, 0); 248 } 249 250 TEST(AccessTests, TestExecute) { 251 AccessType accessType; 252 accessType.execute = true; 253 254 std::filesystem::path path = createTemporaryFile(__func__, accessType); 255 ASSERT_FALSE(path.empty()); 256 257 std::int64_t res = callAccess(path, accessType); 258 259 std::filesystem::remove(path); 260 261 ASSERT_EQ(res, 0); 262 } 263 264 TEST(AccessTests, TestNotExecute) { 265 AccessType accessType; 266 accessType.execute = false; 267 268 std::filesystem::path path = createTemporaryFile(__func__, accessType); 269 ASSERT_FALSE(path.empty()); 270 271 accessType.execute = true; 272 std::int64_t res = callAccess(path, accessType); 273 274 std::filesystem::remove(path); 275 276 ASSERT_NE(res, 0); 277 } 278 279 TEST(AccessTests, TestRWX) { 280 AccessType accessType; 281 accessType.read = true; 282 accessType.write = true; 283 accessType.execute = true; 284 285 std::filesystem::path path = createTemporaryFile(__func__, accessType); 286 ASSERT_FALSE(path.empty()); 287 288 std::int64_t res = callAccess(path, accessType); 289 290 std::filesystem::remove(path); 291 292 ASSERT_EQ(res, 0); 293 } 294 295 TEST(AccessTests, TestNotRWX0) { 296 AccessType accessType; 297 accessType.read = false; 298 accessType.write = false; 299 accessType.execute = false; 300 301 std::filesystem::path path = createTemporaryFile(__func__, accessType); 302 ASSERT_FALSE(path.empty()); 303 304 accessType.read = true; 305 accessType.write = true; 306 accessType.execute = true; 307 std::int64_t res = callAccess(path, accessType); 308 309 std::filesystem::remove(path); 310 311 ASSERT_NE(res, 0); 312 } 313 314 TEST(AccessTests, TestNotRWX1) { 315 AccessType accessType; 316 accessType.read = true; 317 accessType.write = false; 318 accessType.execute = false; 319 320 std::filesystem::path path = createTemporaryFile(__func__, accessType); 321 ASSERT_FALSE(path.empty()); 322 323 accessType.read = true; 324 accessType.write = true; 325 accessType.execute = true; 326 std::int64_t res = callAccess(path, accessType); 327 328 std::filesystem::remove(path); 329 330 ASSERT_NE(res, 0); 331 } 332 333 TEST(AccessTests, TestNotRWX2) { 334 AccessType accessType; 335 accessType.read = true; 336 accessType.write = true; 337 accessType.execute = false; 338 339 std::filesystem::path path = createTemporaryFile(__func__, accessType); 340 ASSERT_FALSE(path.empty()); 341 342 accessType.read = true; 343 accessType.write = true; 344 accessType.execute = true; 345 std::int64_t res = callAccess(path, accessType); 346 347 std::filesystem::remove(path); 348 349 ASSERT_NE(res, 0); 350 } 351 352 TEST(AccessTests, TestNotRWX3) { 353 AccessType accessType; 354 accessType.read = true; 355 accessType.write = false; 356 accessType.execute = true; 357 358 std::filesystem::path path = createTemporaryFile(__func__, accessType); 359 ASSERT_FALSE(path.empty()); 360 361 accessType.read = true; 362 accessType.write = true; 363 accessType.execute = true; 364 std::int64_t res = callAccess(path, accessType); 365 366 std::filesystem::remove(path); 367 368 ASSERT_NE(res, 0); 369 } 370 371 TEST(AccessTests, TestNotRWX4) { 372 AccessType accessType; 373 accessType.read = false; 374 accessType.write = true; 375 accessType.execute = true; 376 377 std::filesystem::path path = createTemporaryFile(__func__, accessType); 378 ASSERT_FALSE(path.empty()); 379 380 accessType.read = true; 381 accessType.write = true; 382 accessType.execute = true; 383 std::int64_t res = callAccess(path, accessType); 384 385 std::filesystem::remove(path); 386 387 ASSERT_NE(res, 0); 388 } 389 390 #endif // !_WIN32 391