1 //===-- runtime/file.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 #include "file.h" 10 #include "tools.h" 11 #include "flang/Runtime/magic-numbers.h" 12 #include "flang/Runtime/memory.h" 13 #include <algorithm> 14 #include <cerrno> 15 #include <cstring> 16 #include <fcntl.h> 17 #include <stdlib.h> 18 #include <sys/stat.h> 19 #ifdef _WIN32 20 #include "flang/Common/windows-include.h" 21 #include <io.h> 22 #else 23 #include <unistd.h> 24 #endif 25 26 namespace Fortran::runtime::io { 27 28 void OpenFile::set_path(OwningPtr<char> &&path, std::size_t bytes) { 29 path_ = std::move(path); 30 pathLength_ = bytes; 31 } 32 33 static int openfile_mkstemp(IoErrorHandler &handler) { 34 #ifdef _WIN32 35 const unsigned int uUnique{0}; 36 // GetTempFileNameA needs a directory name < MAX_PATH-14 characters in length. 37 // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettempfilenamea 38 char tempDirName[MAX_PATH - 14]; 39 char tempFileName[MAX_PATH]; 40 unsigned long nBufferLength{sizeof(tempDirName)}; 41 nBufferLength = ::GetTempPathA(nBufferLength, tempDirName); 42 if (nBufferLength > sizeof(tempDirName) || nBufferLength == 0) { 43 return -1; 44 } 45 if (::GetTempFileNameA(tempDirName, "Fortran", uUnique, tempFileName) == 0) { 46 return -1; 47 } 48 int fd{::_open(tempFileName, _O_CREAT | _O_BINARY | _O_TEMPORARY | _O_RDWR, 49 _S_IREAD | _S_IWRITE)}; 50 #else 51 char path[]{"/tmp/Fortran-Scratch-XXXXXX"}; 52 int fd{::mkstemp(path)}; 53 #endif 54 if (fd < 0) { 55 handler.SignalErrno(); 56 } 57 #ifndef _WIN32 58 ::unlink(path); 59 #endif 60 return fd; 61 } 62 63 void OpenFile::Open(OpenStatus status, Fortran::common::optional<Action> action, 64 Position position, IoErrorHandler &handler) { 65 if (fd_ >= 0 && 66 (status == OpenStatus::Old || status == OpenStatus::Unknown)) { 67 return; 68 } 69 CloseFd(handler); 70 if (status == OpenStatus::Scratch) { 71 if (path_.get()) { 72 handler.SignalError("FILE= must not appear with STATUS='SCRATCH'"); 73 path_.reset(); 74 } 75 if (!action) { 76 action = Action::ReadWrite; 77 } 78 fd_ = openfile_mkstemp(handler); 79 } else { 80 if (!path_.get()) { 81 handler.SignalError("FILE= is required"); 82 return; 83 } 84 int flags{0}; 85 #ifdef _WIN32 86 // We emit explicit CR+LF line endings and cope with them on input 87 // for formatted files, since we can't yet always know now at OPEN 88 // time whether the file is formatted or not. 89 flags |= O_BINARY; 90 #endif 91 if (status != OpenStatus::Old) { 92 flags |= O_CREAT; 93 } 94 if (status == OpenStatus::New) { 95 flags |= O_EXCL; 96 } else if (status == OpenStatus::Replace) { 97 flags |= O_TRUNC; 98 } 99 if (!action) { 100 // Try to open read/write, back off to read-only or even write-only 101 // on failure 102 fd_ = ::open(path_.get(), flags | O_RDWR, 0600); 103 if (fd_ >= 0) { 104 action = Action::ReadWrite; 105 } else { 106 fd_ = ::open(path_.get(), flags | O_RDONLY, 0600); 107 if (fd_ >= 0) { 108 action = Action::Read; 109 } else { 110 action = Action::Write; 111 } 112 } 113 } 114 if (fd_ < 0) { 115 switch (*action) { 116 case Action::Read: 117 flags |= O_RDONLY; 118 break; 119 case Action::Write: 120 flags |= O_WRONLY; 121 break; 122 case Action::ReadWrite: 123 flags |= O_RDWR; 124 break; 125 } 126 fd_ = ::open(path_.get(), flags, 0600); 127 if (fd_ < 0) { 128 handler.SignalErrno(); 129 } 130 } 131 } 132 RUNTIME_CHECK(handler, action.has_value()); 133 pending_.reset(); 134 if (fd_ >= 0 && position == Position::Append && !RawSeekToEnd()) { 135 handler.SignalError(IostatOpenBadAppend); 136 } 137 isTerminal_ = fd_ >= 0 && IsATerminal(fd_); 138 mayRead_ = *action != Action::Write; 139 mayWrite_ = *action != Action::Read; 140 if (status == OpenStatus::Old || status == OpenStatus::Unknown) { 141 knownSize_.reset(); 142 #ifndef _WIN32 143 struct stat buf; 144 if (fd_ >= 0 && ::fstat(fd_, &buf) == 0) { 145 mayPosition_ = S_ISREG(buf.st_mode); 146 knownSize_ = buf.st_size; 147 } 148 #else // TODO: _WIN32 149 mayPosition_ = true; 150 #endif 151 } else { 152 knownSize_ = 0; 153 mayPosition_ = true; 154 } 155 openPosition_ = position; // for INQUIRE(POSITION=) 156 } 157 158 void OpenFile::Predefine(int fd) { 159 fd_ = fd; 160 path_.reset(); 161 pathLength_ = 0; 162 position_ = 0; 163 knownSize_.reset(); 164 nextId_ = 0; 165 pending_.reset(); 166 isTerminal_ = fd == 2 || IsATerminal(fd_); 167 mayRead_ = fd == 0; 168 mayWrite_ = fd != 0; 169 mayPosition_ = false; 170 #ifdef _WIN32 171 isWindowsTextFile_ = true; 172 #endif 173 } 174 175 void OpenFile::Close(CloseStatus status, IoErrorHandler &handler) { 176 pending_.reset(); 177 knownSize_.reset(); 178 switch (status) { 179 case CloseStatus::Keep: 180 break; 181 case CloseStatus::Delete: 182 if (path_.get()) { 183 ::unlink(path_.get()); 184 } 185 break; 186 } 187 path_.reset(); 188 CloseFd(handler); 189 } 190 191 std::size_t OpenFile::Read(FileOffset at, char *buffer, std::size_t minBytes, 192 std::size_t maxBytes, IoErrorHandler &handler) { 193 if (maxBytes == 0) { 194 return 0; 195 } 196 CheckOpen(handler); 197 if (!Seek(at, handler)) { 198 return 0; 199 } 200 minBytes = std::min(minBytes, maxBytes); 201 std::size_t got{0}; 202 while (got < minBytes) { 203 auto chunk{::read(fd_, buffer + got, maxBytes - got)}; 204 if (chunk == 0) { 205 break; 206 } else if (chunk < 0) { 207 auto err{errno}; 208 if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) { 209 handler.SignalError(err); 210 break; 211 } 212 } else { 213 SetPosition(position_ + chunk); 214 got += chunk; 215 } 216 } 217 return got; 218 } 219 220 std::size_t OpenFile::Write(FileOffset at, const char *buffer, 221 std::size_t bytes, IoErrorHandler &handler) { 222 if (bytes == 0) { 223 return 0; 224 } 225 CheckOpen(handler); 226 if (!Seek(at, handler)) { 227 return 0; 228 } 229 std::size_t put{0}; 230 while (put < bytes) { 231 auto chunk{::write(fd_, buffer + put, bytes - put)}; 232 if (chunk >= 0) { 233 SetPosition(position_ + chunk); 234 put += chunk; 235 } else { 236 auto err{errno}; 237 if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) { 238 handler.SignalError(err); 239 break; 240 } 241 } 242 } 243 if (knownSize_ && position_ > *knownSize_) { 244 knownSize_ = position_; 245 } 246 return put; 247 } 248 249 inline static int openfile_ftruncate(int fd, OpenFile::FileOffset at) { 250 #ifdef _WIN32 251 return ::_chsize(fd, at); 252 #else 253 return ::ftruncate(fd, at); 254 #endif 255 } 256 257 void OpenFile::Truncate(FileOffset at, IoErrorHandler &handler) { 258 CheckOpen(handler); 259 if (!knownSize_ || *knownSize_ != at) { 260 if (openfile_ftruncate(fd_, at) != 0) { 261 handler.SignalErrno(); 262 } 263 knownSize_ = at; 264 } 265 } 266 267 // The operation is performed immediately; the results are saved 268 // to be claimed by a later WAIT statement. 269 // TODO: True asynchronicity 270 int OpenFile::ReadAsynchronously( 271 FileOffset at, char *buffer, std::size_t bytes, IoErrorHandler &handler) { 272 CheckOpen(handler); 273 int iostat{0}; 274 for (std::size_t got{0}; got < bytes;) { 275 #if _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L 276 auto chunk{::pread(fd_, buffer + got, bytes - got, at)}; 277 #else 278 auto chunk{Seek(at, handler) ? ::read(fd_, buffer + got, bytes - got) : -1}; 279 #endif 280 if (chunk == 0) { 281 iostat = FORTRAN_RUNTIME_IOSTAT_END; 282 break; 283 } 284 if (chunk < 0) { 285 auto err{errno}; 286 if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) { 287 iostat = err; 288 break; 289 } 290 } else { 291 at += chunk; 292 got += chunk; 293 } 294 } 295 return PendingResult(handler, iostat); 296 } 297 298 // TODO: True asynchronicity 299 int OpenFile::WriteAsynchronously(FileOffset at, const char *buffer, 300 std::size_t bytes, IoErrorHandler &handler) { 301 CheckOpen(handler); 302 int iostat{0}; 303 for (std::size_t put{0}; put < bytes;) { 304 #if _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L 305 auto chunk{::pwrite(fd_, buffer + put, bytes - put, at)}; 306 #else 307 auto chunk{ 308 Seek(at, handler) ? ::write(fd_, buffer + put, bytes - put) : -1}; 309 #endif 310 if (chunk >= 0) { 311 at += chunk; 312 put += chunk; 313 } else { 314 auto err{errno}; 315 if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) { 316 iostat = err; 317 break; 318 } 319 } 320 } 321 return PendingResult(handler, iostat); 322 } 323 324 void OpenFile::Wait(int id, IoErrorHandler &handler) { 325 Fortran::common::optional<int> ioStat; 326 Pending *prev{nullptr}; 327 for (Pending *p{pending_.get()}; p; p = (prev = p)->next.get()) { 328 if (p->id == id) { 329 ioStat = p->ioStat; 330 if (prev) { 331 prev->next.reset(p->next.release()); 332 } else { 333 pending_.reset(p->next.release()); 334 } 335 break; 336 } 337 } 338 if (ioStat) { 339 handler.SignalError(*ioStat); 340 } 341 } 342 343 void OpenFile::WaitAll(IoErrorHandler &handler) { 344 while (true) { 345 int ioStat; 346 if (pending_) { 347 ioStat = pending_->ioStat; 348 pending_.reset(pending_->next.release()); 349 } else { 350 return; 351 } 352 handler.SignalError(ioStat); 353 } 354 } 355 356 Position OpenFile::InquirePosition() const { 357 if (openPosition_) { // from OPEN statement 358 return *openPosition_; 359 } else { // unit has been repositioned since opening 360 if (position_ == knownSize_.value_or(position_ + 1)) { 361 return Position::Append; 362 } else if (position_ == 0 && mayPosition_) { 363 return Position::Rewind; 364 } else { 365 return Position::AsIs; // processor-dependent & no common behavior 366 } 367 } 368 } 369 370 void OpenFile::CheckOpen(const Terminator &terminator) { 371 RUNTIME_CHECK(terminator, fd_ >= 0); 372 } 373 374 bool OpenFile::Seek(FileOffset at, IoErrorHandler &handler) { 375 if (at == position_) { 376 return true; 377 } else if (RawSeek(at)) { 378 SetPosition(at); 379 return true; 380 } else { 381 handler.SignalError(IostatCannotReposition); 382 return false; 383 } 384 } 385 386 bool OpenFile::RawSeek(FileOffset at) { 387 #ifdef _LARGEFILE64_SOURCE 388 return ::lseek64(fd_, at, SEEK_SET) == at; 389 #else 390 return ::lseek(fd_, at, SEEK_SET) == at; 391 #endif 392 } 393 394 bool OpenFile::RawSeekToEnd() { 395 #ifdef _LARGEFILE64_SOURCE 396 std::int64_t at{::lseek64(fd_, 0, SEEK_END)}; 397 #else 398 std::int64_t at{::lseek(fd_, 0, SEEK_END)}; 399 #endif 400 if (at >= 0) { 401 knownSize_ = at; 402 return true; 403 } else { 404 return false; 405 } 406 } 407 408 int OpenFile::PendingResult(const Terminator &terminator, int iostat) { 409 int id{nextId_++}; 410 pending_ = New<Pending>{terminator}(id, iostat, std::move(pending_)); 411 return id; 412 } 413 414 void OpenFile::CloseFd(IoErrorHandler &handler) { 415 if (fd_ >= 0) { 416 if (fd_ <= 2) { 417 // don't actually close a standard file descriptor, we might need it 418 } else { 419 if (::close(fd_) != 0) { 420 handler.SignalErrno(); 421 } 422 } 423 fd_ = -1; 424 } 425 } 426 427 #if !defined(RT_DEVICE_COMPILATION) 428 bool IsATerminal(int fd) { return ::isatty(fd); } 429 430 #if defined(_WIN32) && !defined(F_OK) 431 // Access flags are normally defined in unistd.h, which unavailable under 432 // Windows. Instead, define the flags as documented at 433 // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/access-waccess 434 // On Mingw, io.h does define these same constants - so check whether they 435 // already are defined before defining these. 436 #define F_OK 00 437 #define W_OK 02 438 #define R_OK 04 439 #endif 440 441 bool IsExtant(const char *path) { return ::access(path, F_OK) == 0; } 442 bool MayRead(const char *path) { return ::access(path, R_OK) == 0; } 443 bool MayWrite(const char *path) { return ::access(path, W_OK) == 0; } 444 bool MayReadAndWrite(const char *path) { 445 return ::access(path, R_OK | W_OK) == 0; 446 } 447 448 std::int64_t SizeInBytes(const char *path) { 449 #ifndef _WIN32 450 struct stat buf; 451 if (::stat(path, &buf) == 0) { 452 return buf.st_size; 453 } 454 #else // TODO: _WIN32 455 #endif 456 // No Fortran compiler signals an error 457 return -1; 458 } 459 #else // defined(RT_DEVICE_COMPILATION) 460 RT_API_ATTRS bool IsATerminal(int fd) { 461 Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION); 462 } 463 RT_API_ATTRS bool IsExtant(const char *path) { 464 Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION); 465 } 466 RT_API_ATTRS bool MayRead(const char *path) { 467 Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION); 468 } 469 RT_API_ATTRS bool MayWrite(const char *path) { 470 Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION); 471 } 472 RT_API_ATTRS bool MayReadAndWrite(const char *path) { 473 Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION); 474 } 475 RT_API_ATTRS std::int64_t SizeInBytes(const char *path) { 476 Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION); 477 } 478 #endif // defined(RT_DEVICE_COMPILATION) 479 480 } // namespace Fortran::runtime::io 481