brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.7 KiB · 8255ec8 Raw
489 lines · cpp
1//===-- lib/runtime/file.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#include "flang-rt/runtime/file.h"10#include "flang-rt/runtime/memory.h"11#include "flang-rt/runtime/tools.h"12#include "flang/Runtime/magic-numbers.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 _WIN3220#include "flang/Common/windows-include.h"21#include <io.h>22#else23#include <unistd.h>24#endif25 26namespace Fortran::runtime::io {27 28void OpenFile::set_path(OwningPtr<char> &&path, std::size_t bytes) {29  path_ = std::move(path);30  pathLength_ = bytes;31}32 33static int openfile_mkstemp(IoErrorHandler &handler) {34#ifdef _WIN3235  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-gettempfilenamea38  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#else51  char path[]{"/tmp/Fortran-Scratch-XXXXXX"};52  int fd{::mkstemp(path)};53#endif54  if (fd < 0) {55    handler.SignalErrno();56  }57#ifndef _WIN3258  ::unlink(path);59#endif60  return fd;61}62 63void OpenFile::Open(OpenStatus status, common::optional<Action> action,64    Position position, IoErrorHandler &handler) {65  if (fd_ >= 0 &&66      (status == OpenStatus::Old || status == OpenStatus::Unknown)) {67    if (position == Position::Rewind) {68      Seek(0, handler);69    } else if (position == Position::Append) {70      SeekToEnd(handler);71    }72    openPosition_ = position; // for INQUIRE(POSITION=)73    return;74  }75  CloseFd(handler);76  if (status == OpenStatus::Scratch) {77    if (path_.get()) {78      handler.SignalError("FILE= must not appear with STATUS='SCRATCH'");79      path_.reset();80    }81    if (!action) {82      action = Action::ReadWrite;83    }84    fd_ = openfile_mkstemp(handler);85  } else {86    if (!path_.get()) {87      handler.SignalError("FILE= is required");88      return;89    }90    int flags{0};91#ifdef _WIN3292    // We emit explicit CR+LF line endings and cope with them on input93    // for formatted files, since we can't yet always know now at OPEN94    // time whether the file is formatted or not.95    flags |= O_BINARY;96#endif97    if (status != OpenStatus::Old) {98      flags |= O_CREAT;99    }100    if (status == OpenStatus::New) {101      flags |= O_EXCL;102    } else if (status == OpenStatus::Replace) {103      flags |= O_TRUNC;104    }105    if (!action) {106      // Try to open read/write, back off to read-only or even write-only107      // on failure108      fd_ = ::open(path_.get(), flags | O_RDWR, 0600);109      if (fd_ >= 0) {110        action = Action::ReadWrite;111      } else {112        fd_ = ::open(path_.get(), flags | O_RDONLY, 0600);113        if (fd_ >= 0) {114          action = Action::Read;115        } else {116          action = Action::Write;117        }118      }119    }120    if (fd_ < 0) {121      switch (*action) {122      case Action::Read:123        flags |= O_RDONLY;124        break;125      case Action::Write:126        flags |= O_WRONLY;127        break;128      case Action::ReadWrite:129        flags |= O_RDWR;130        break;131      }132      fd_ = ::open(path_.get(), flags, 0600);133      if (fd_ < 0) {134        handler.SignalErrno();135      }136    }137  }138  RUNTIME_CHECK(handler, action.has_value());139  pending_.reset();140  if (fd_ >= 0 && position == Position::Append) {141    SeekToEnd(handler);142  }143  isTerminal_ = fd_ >= 0 && IsATerminal(fd_);144  mayRead_ = *action != Action::Write;145  mayWrite_ = *action != Action::Read;146  if (status == OpenStatus::Old || status == OpenStatus::Unknown) {147    knownSize_.reset();148#ifndef _WIN32149    struct stat buf;150    if (fd_ >= 0 && ::fstat(fd_, &buf) == 0) {151      mayPosition_ = S_ISREG(buf.st_mode);152      knownSize_ = buf.st_size;153    }154#else // TODO: _WIN32155    mayPosition_ = true;156#endif157  } else {158    knownSize_ = 0;159    mayPosition_ = true;160  }161  openPosition_ = position; // for INQUIRE(POSITION=)162}163 164void OpenFile::Predefine(int fd) {165  fd_ = fd;166  path_.reset();167  pathLength_ = 0;168  position_ = 0;169  knownSize_.reset();170  nextId_ = 0;171  pending_.reset();172  isTerminal_ = fd == 2 || IsATerminal(fd_);173  mayRead_ = fd == 0;174  mayWrite_ = fd != 0;175  mayPosition_ = false;176#ifdef _WIN32177  isWindowsTextFile_ = true;178#endif179}180 181void OpenFile::Close(CloseStatus status, IoErrorHandler &handler) {182  pending_.reset();183  knownSize_.reset();184  switch (status) {185  case CloseStatus::Keep:186    break;187  case CloseStatus::Delete:188    if (path_.get()) {189      ::unlink(path_.get());190    }191    break;192  }193  path_.reset();194  CloseFd(handler);195}196 197std::size_t OpenFile::Read(FileOffset at, char *buffer, std::size_t minBytes,198    std::size_t maxBytes, IoErrorHandler &handler) {199  if (maxBytes == 0) {200    return 0;201  }202  CheckOpen(handler);203  if (!Seek(at, handler)) {204    return 0;205  }206  minBytes = std::min(minBytes, maxBytes);207  std::size_t got{0};208  while (got < minBytes) {209    auto chunk{::read(fd_, buffer + got, maxBytes - got)};210    if (chunk == 0) {211      break;212    } else if (chunk < 0) {213      auto err{errno};214      if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {215        handler.SignalError(err);216        break;217      }218    } else {219      SetPosition(position_ + chunk);220      got += chunk;221    }222  }223  return got;224}225 226std::size_t OpenFile::Write(FileOffset at, const char *buffer,227    std::size_t bytes, IoErrorHandler &handler) {228  if (bytes == 0) {229    return 0;230  }231  CheckOpen(handler);232  if (!Seek(at, handler)) {233    return 0;234  }235  std::size_t put{0};236  while (put < bytes) {237    auto chunk{::write(fd_, buffer + put, bytes - put)};238    if (chunk >= 0) {239      SetPosition(position_ + chunk);240      put += chunk;241    } else {242      auto err{errno};243      if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {244        handler.SignalError(err);245        break;246      }247    }248  }249  if (knownSize_ && position_ > *knownSize_) {250    knownSize_ = position_;251  }252  return put;253}254 255inline static int openfile_ftruncate(int fd, OpenFile::FileOffset at) {256#ifdef _WIN32257  return ::_chsize(fd, at);258#else259  return ::ftruncate(fd, at);260#endif261}262 263void OpenFile::Truncate(FileOffset at, IoErrorHandler &handler) {264  CheckOpen(handler);265  if (!knownSize_ || *knownSize_ != at) {266    if (openfile_ftruncate(fd_, at) != 0) {267      handler.SignalErrno();268    }269    knownSize_ = at;270  }271}272 273// The operation is performed immediately; the results are saved274// to be claimed by a later WAIT statement.275// TODO: True asynchronicity276int OpenFile::ReadAsynchronously(277    FileOffset at, char *buffer, std::size_t bytes, IoErrorHandler &handler) {278  CheckOpen(handler);279  int iostat{0};280  for (std::size_t got{0}; got < bytes;) {281#if _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L282    auto chunk{::pread(fd_, buffer + got, bytes - got, at)};283#else284    auto chunk{Seek(at, handler) ? ::read(fd_, buffer + got, bytes - got) : -1};285#endif286    if (chunk == 0) {287      iostat = FORTRAN_RUNTIME_IOSTAT_END;288      break;289    }290    if (chunk < 0) {291      auto err{errno};292      if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {293        iostat = err;294        break;295      }296    } else {297      at += chunk;298      got += chunk;299    }300  }301  return PendingResult(handler, iostat);302}303 304// TODO: True asynchronicity305int OpenFile::WriteAsynchronously(FileOffset at, const char *buffer,306    std::size_t bytes, IoErrorHandler &handler) {307  CheckOpen(handler);308  int iostat{0};309  for (std::size_t put{0}; put < bytes;) {310#if _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L311    auto chunk{::pwrite(fd_, buffer + put, bytes - put, at)};312#else313    auto chunk{314        Seek(at, handler) ? ::write(fd_, buffer + put, bytes - put) : -1};315#endif316    if (chunk >= 0) {317      at += chunk;318      put += chunk;319    } else {320      auto err{errno};321      if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {322        iostat = err;323        break;324      }325    }326  }327  return PendingResult(handler, iostat);328}329 330void OpenFile::Wait(int id, IoErrorHandler &handler) {331  common::optional<int> ioStat;332  Pending *prev{nullptr};333  for (Pending *p{pending_.get()}; p; p = (prev = p)->next.get()) {334    if (p->id == id) {335      ioStat = p->ioStat;336      if (prev) {337        prev->next.reset(p->next.release());338      } else {339        pending_.reset(p->next.release());340      }341      break;342    }343  }344  if (ioStat) {345    handler.SignalError(*ioStat);346  }347}348 349void OpenFile::WaitAll(IoErrorHandler &handler) {350  while (true) {351    int ioStat;352    if (pending_) {353      ioStat = pending_->ioStat;354      pending_.reset(pending_->next.release());355    } else {356      return;357    }358    handler.SignalError(ioStat);359  }360}361 362Position OpenFile::InquirePosition(FileOffset offset) const {363  if (openPosition_) { // from OPEN statement364    return *openPosition_;365  } else { // unit has been repositioned since opening366    if (offset == knownSize_.value_or(offset + 1)) {367      return Position::Append;368    } else if (offset == 0 && mayPosition_) {369      return Position::Rewind;370    } else {371      return Position::AsIs; // processor-dependent & no common behavior372    }373  }374}375 376void OpenFile::CheckOpen(const Terminator &terminator) {377  RUNTIME_CHECK(terminator, fd_ >= 0);378}379 380bool OpenFile::Seek(FileOffset at, IoErrorHandler &handler) {381  if (at == position_) {382    return true;383  } else if (RawSeek(at)) {384    SetPosition(at);385    return true;386  } else {387    handler.SignalError(IostatCannotReposition);388    return false;389  }390}391 392bool OpenFile::RawSeek(FileOffset at) {393#ifdef _LARGEFILE64_SOURCE394  return ::lseek64(fd_, at, SEEK_SET) == at;395#else396  return ::lseek(fd_, at, SEEK_SET) == at;397#endif398}399 400bool OpenFile::SeekToEnd(IoErrorHandler &handler) {401#ifdef _LARGEFILE64_SOURCE402  std::int64_t at{::lseek64(fd_, 0, SEEK_END)};403#else404  std::int64_t at{::lseek(fd_, 0, SEEK_END)};405#endif406  if (at >= 0) {407    knownSize_ = at;408    SetPosition(at);409    return true;410  } else {411    handler.SignalError(IostatOpenBadAppend);412    return false;413  }414}415 416int OpenFile::PendingResult(const Terminator &terminator, int iostat) {417  int id{nextId_++};418  pending_ = New<Pending>{terminator}(id, iostat, std::move(pending_));419  return id;420}421 422void OpenFile::CloseFd(IoErrorHandler &handler) {423  if (fd_ >= 0) {424    if (fd_ <= 2) {425      // don't actually close a standard file descriptor, we might need it426    } else {427      if (::close(fd_) != 0) {428        handler.SignalErrno();429      }430    }431    fd_ = -1;432  }433}434 435#if !defined(RT_DEVICE_COMPILATION)436bool IsATerminal(int fd) { return ::isatty(fd); }437 438#if defined(_WIN32) && !defined(F_OK)439// Access flags are normally defined in unistd.h, which unavailable under440// Windows. Instead, define the flags as documented at441// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/access-waccess442// On Mingw, io.h does define these same constants - so check whether they443// already are defined before defining these.444#define F_OK 00445#define W_OK 02446#define R_OK 04447#endif448 449bool IsExtant(const char *path) { return ::access(path, F_OK) == 0; }450bool MayRead(const char *path) { return ::access(path, R_OK) == 0; }451bool MayWrite(const char *path) { return ::access(path, W_OK) == 0; }452bool MayReadAndWrite(const char *path) {453  return ::access(path, R_OK | W_OK) == 0;454}455 456std::int64_t SizeInBytes(const char *path) {457#ifndef _WIN32458  struct stat buf;459  if (::stat(path, &buf) == 0) {460    return buf.st_size;461  }462#else // TODO: _WIN32463#endif464  // No Fortran compiler signals an error465  return -1;466}467#else // defined(RT_DEVICE_COMPILATION)468RT_API_ATTRS bool IsATerminal(int fd) {469  Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);470}471RT_API_ATTRS bool IsExtant(const char *path) {472  Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);473}474RT_API_ATTRS bool MayRead(const char *path) {475  Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);476}477RT_API_ATTRS bool MayWrite(const char *path) {478  Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);479}480RT_API_ATTRS bool MayReadAndWrite(const char *path) {481  Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);482}483RT_API_ATTRS std::int64_t SizeInBytes(const char *path) {484  Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);485}486#endif // defined(RT_DEVICE_COMPILATION)487 488} // namespace Fortran::runtime::io489